package com.zmj.sy.mom.srv.aps.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zmj.sy.mom.srv.aps.bean.dto.feign.sinocam.SinoCamCommonsResDto;
import com.zmj.sy.mom.srv.aps.bean.dto.feign.sinocam.SinoCamPushLoftReqDto;
import com.zmj.sy.mom.srv.aps.bean.entity.base.BaseEntity;
import com.zmj.sy.mom.srv.aps.bean.entity.order.*;
import com.zmj.sy.mom.srv.aps.bean.entity.resource.*;
import com.zmj.sy.mom.srv.aps.bean.entity.third.SinoCam;
import com.zmj.sy.mom.srv.aps.bean.entity.third.SinoCamPushDrawing;
import com.zmj.sy.mom.srv.aps.bean.vo.base.*;
import com.zmj.sy.mom.srv.aps.bean.vo.mbd.*;
import com.zmj.sy.mom.srv.aps.bean.vo.mbd.factory.MbdBalanceRateDataListOfFactoryResVo;
import com.zmj.sy.mom.srv.aps.bean.vo.mbd.factory.MbdBalanceRateOfFactoryReqVo;
import com.zmj.sy.mom.srv.aps.bean.vo.mbd.factory.MbdBalanceRateOfFactoryResVo;
import com.zmj.sy.mom.srv.aps.bean.vo.mbd.group.*;
import com.zmj.sy.mom.srv.aps.bean.vo.mbd.plan.MbdPlanMonthlyReqVo;
import com.zmj.sy.mom.srv.aps.bean.vo.mbd.plan.MbdPlanMonthlyResVo;
import com.zmj.sy.mom.srv.aps.bean.vo.plan.PlanMonthlyProcessDateResVo;
import com.zmj.sy.mom.srv.aps.feign.SinoCamApiClient;
import com.zmj.sy.mom.srv.aps.mapper.*;
import com.zmj.sy.mom.srv.aps.utils.JsonUtils;
import com.zmj.sy.mom.srv.aps.utils.MkBeanUtils;
import com.zmj.sy.mom.srv.aps.utils.SyExceptionUtils;
import com.zmj.sy.mom.srv.aps.utils.WorkshopProp;
import com.zmj.sy.mom.srv.mbd.api.api.MomSrvMbdApiClient;
import com.zmj.sy.mom.srv.mbd.api.bean.dto.BaseListResDto;
import com.zmj.sy.mom.srv.mbd.api.bean.dto.bominfo.BomInfoSelectResDto;
import com.zmj.sy.mom.srv.mbd.api.bean.dto.mesdxf.MesDxfConfirmDataReqDto;
import com.zmj.sy.mom.srv.mbd.api.bean.dto.mesdxf.MesDxfConfirmReqDto;
import com.zmj.sy.mom.srv.mbd.api.bean.dto.mesdxf.MesDxfFindResDto;
import com.zmj.sy.mom.srv.mbd.api.bean.vo.RobotWeldingWireUsageVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFColor;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Slf4j
@RequiredArgsConstructor
@Service
public class MbdService {

    private final OrderMapper orderMapper;

    private final OrderBomMapper orderBomMapper;

    private final OrderPhaseMapper orderPhaseMapper;

    private final OrderAccessoriesMapper orderAccessoriesMapper;

//    private final StationGroupMapper stationGroupMapper;

    private final GroupMapper groupMapper;

    private final StationMapper stationMapper;

    private final MbdBalanceRateMapper mbdBalanceRateMapper;

    private final MbdBalanceRateItemMapper mbdBalanceRateItemMapper;

    private final MomSrvMbdApiClient momSrvMbdApiClient;

    private final ImportMbdDxfMapper importMbdDxfMapper;

    private final SinoCamMapper sinoCamMapper;

    private final SinoCamPushDrawingMapper sinoCamPushDrawingMapper;

    private final SinoCamApiClient sinoCamApiClient;

    private final DictDataMapper dictDataMapper;

    private final MbdBalanceRateDataMapper mbdBalanceRateDataMapper;

    private final MbdBalanceRateOpMapper mbdBalanceRateOpMapper;

    private final ObjectMapper objectMapper;

    private final PhaseMapper phaseMapper;

    private final ShiftMapper shiftMapper;

    private final ApsWorkDetailMapper apsWorkDetailMapper;
    private final ApsWorkOrderMapper apsWorkOrderMapper;


    public BasePageResVo<MbdListResVo> list(BasePageReqVo reqVo) {

        LambdaQueryWrapper<Order> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(Order::getOrderType, 2);
        Page<Order> orderPage = orderMapper.selectPage(reqVo.toPage(), wrapper);

        return BasePageResVo.of(orderPage, MbdListResVo.class);
    }

    @Transactional
    public void edit(MbdEditReqVo reqVo) {

        UpdateWrapper<Order> wrapper = Wrappers.update();
        wrapper.eq("id", reqVo.getId());
        wrapper.set("order_qty", reqVo.getOrderQty());
        orderMapper.update(null, wrapper);
    }

    public MbdBalanceRateResVo balanceRate(MbdBalanceRateReqVo reqVo) {
        Order order = orderMapper.selectOneAll(reqVo.getId());

        String pattern = "^(?<prefix>.+)" + WorkshopProp.PH_PATTERN+"$";
        if (!order.getMaterCode().matches(pattern)) {
            throw SyExceptionUtils.e("只有P*结尾的物料才能计算生产平衡率");
        }
        List<OrderBom> orderBomList = orderBomMapper.listByQuery(Wrappers.query().eq("order_id", order.getId()));
        List<OrderPhase> orderPhaseList = orderPhaseMapper.listByQuery(Wrappers.query().eq("order_id", order.getId()));
        Map<String, List<Station>> stationCount = getStationCount(orderPhaseList);

        Map<Integer, List<OrderPhase>> orderPhaseMap = orderPhaseList.stream().collect(Collectors.groupingBy(OrderPhase::getBomId));
        Map<String, OrderBom> orderBomMap = orderBomList.stream().collect(Collectors.toMap(OrderBom::getMaterCode, Function.identity(), (e1, e2) -> e1));

        MbdBalanceRateResVo resVo = new MbdBalanceRateResVo();

        String preFix = order.getMaterCode().substring(0, order.getMaterCode().length() - 2);


        MbdBalanceRate mbdBalanceRate = mbdBalanceRateMapper.selectOne(Wrappers.<MbdBalanceRate>lambdaQuery().eq(MbdBalanceRate::getOrderId, order.getId()));
        resVo.setName(getBalanceRateName(mbdBalanceRate));
        resVo.setOrderQty(order.getOrderQty());

        // 订单焊丝
        Map<Integer, Map<Integer, OrderAccessories>> wires = getOrderWeldingWire(reqVo.getId());

        applyChange(reqVo);

        MbdBalanceRateDataResVo pro = getBalanceRateDataResVo(order, preFix, orderBomMap, orderPhaseMap, stationCount, null, null, reqVo.getProPhase(), wires);
        resVo.setPro(pro);

        List<MbdBalanceRateItem> mbdBalanceRateItems = getConfigItems(reqVo.getId());
        MbdBalanceRateDataResVo customize = getBalanceRateDataResVo(order, preFix, orderBomMap, orderPhaseMap, stationCount, mbdBalanceRateItems.stream().collect(Collectors.toMap(MbdBalanceRateItem::getPhaseId, Function.identity())), pro, reqVo.getCustomPhase(), wires);
        resVo.setCustomize(customize);

        return resVo;

    }

    private List<MbdBalanceRateItem> getConfigItems(Integer orderId) {
        List<MbdBalanceRateItem> mbdBalanceRateItems = getItems(orderId, 1);
        if (mbdBalanceRateItems.isEmpty()) {
            BaseIdReqVo reqVo = new BaseIdReqVo();
            reqVo.setId(orderId);
            getBalanceConfig(reqVo);
            return getItems(orderId, 1);
        } else {
            return mbdBalanceRateItems;
        }
    }

    private void applyChange(MbdBalanceRateReqVo reqVo) {
        Map<String, List<MbdBalanceRateDataPhaseResVo>> phase = reqVo.getProPhase();
        MbdBalanceRateDataPhaseTimeChange change = reqVo.getWorkTimeChange();
        if (phase == null || change == null) {
            return;
        }
        List<MbdBalanceRateDataPhaseResVo> changedStage = phase.get(change.getChangedStage());
        List<MbdBalanceRateDataPhaseResVo> influencedStage = phase.get(change.getInfluencedStage());
        if (changedStage == null || influencedStage == null) {
            return;
        }
        MbdBalanceRateDataPhaseResVo changedPhase = changedStage.stream().filter(e -> !e.isHidden() && e.getPhaseCode().equals(change.getChangedPhaseCode())).findFirst().orElse(null);
        MbdBalanceRateDataPhaseResVo influencedPhase = influencedStage.stream().filter(e -> !e.isHidden() && e.getPhaseCode().equals(change.getInfluencedPhaseCode())).findFirst().orElse(null);
        if (changedPhase == null || influencedPhase == null) {
            return;
        }
        changedPhase.setWorkTimeMinutes(changedPhase.getWorkTimeMinutes().add(change.getChangedValue()));
        influencedPhase.setWorkTimeMinutes(influencedPhase.getWorkTimeMinutes().subtract(change.getChangedValue()));
        this.saveOp(reqVo.getId(), reqVo.getWorkTimeChange(), changedPhase, influencedPhase);
    }

    private void saveOp(Integer orderId, MbdBalanceRateDataPhaseTimeChange workTimeChange, MbdBalanceRateDataPhaseResVo changedPhase, MbdBalanceRateDataPhaseResVo influencedPhase) {
        MbdBalanceRateOp op = new MbdBalanceRateOp();
        op.setOrderId(orderId);
        op.setChangedStage(workTimeChange.getChangedStage());
        op.setChangedPhaseCode(workTimeChange.getChangedPhaseCode());
        op.setChangedPhaseName(changedPhase.getPhaseName());
        op.setChangedValue(workTimeChange.getChangedValue());
        op.setInfluencedPhaseCode(workTimeChange.getInfluencedPhaseCode());
        op.setInfluencedStage(workTimeChange.getInfluencedStage());
        op.setInfluencedPhaseName(influencedPhase.getPhaseName());
        op.setDimensionType(1);
        mbdBalanceRateOpMapper.insert(op);
    }

    private Map<Integer, Map<Integer, OrderAccessories>> getOrderWeldingWire(Integer orderId) {
        return orderAccessoriesMapper.lambdaQuery()
                .eq(OrderAccessories::getOrderId, orderId)
                .eq(OrderAccessories::getType, 1)
                .list().stream()
                .collect(Collectors.groupingBy(OrderAccessories::getBomId,
                        Collectors.toMap(OrderAccessories::getPhaseId, Function.identity(), (e1, e2) -> e1)));
    }

    private MbdBalanceRateDataResVo getBalanceRateDataResVo(Order order, String preFix, Map<String, OrderBom> orderBomMap, Map<Integer, List<OrderPhase>> orderPhaseMap, Map<String, List<Station>> stationCount, Map<Integer, MbdBalanceRateItem> phaseIdMap, MbdBalanceRateDataResVo pro, Map<String, List<MbdBalanceRateDataPhaseResVo>> phase, Map<Integer, Map<Integer, OrderAccessories>> wires) {


        MbdBalanceRateDataResVo resVo = new MbdBalanceRateDataResVo();

        String patternStr = preFix + WorkshopProp.PH_PATTERN;
        List<OrderBom> collect = orderBomMap.values().stream().filter(e -> e.getMaterCode().matches(patternStr)).sorted(Comparator.comparing(OrderBom::getLevel)).collect(Collectors.toList());

        List<DictData> weldingWireOmitted = dictDataMapper.findByDictTypeCode(Wrappers.<DictData>query().eq("dt.dict_code", "orderPhaseNoneWeldingWire"));

        for (OrderBom orderBom : collect) {
            String simpleNo = orderBom.getMaterCode().replaceFirst(preFix, "");
            List<MbdBalanceRateDataPhaseResVo> proPhaseResVoList = pro == null ? null : pro.getPhase().get(simpleNo);
            if (phase != null && !CollectionUtils.isEmpty(phase.get(simpleNo))) {
                resVo.getPhase().put(simpleNo, phase.get(simpleNo).stream()
                        .map(e -> initialPhaseResVo(e, stationCount, proPhaseResVoList, order, orderBom, orderPhaseMap, wires, weldingWireOmitted))
                        .collect(Collectors.toList()));
                continue;
            }
            List<OrderPhase> orderPhaseList1 = orderPhaseMap.get(orderBom.getId());
            if (CollUtil.isEmpty(orderPhaseList1)) {
                throw SyExceptionUtils.e("物料" + orderBom.getMaterCode() + "没有工序");
            }
            orderPhaseList1.sort(Comparator.comparing(OrderPhase::getPhaseSeq));
            resVo.getPhase().put(simpleNo, orderPhaseList1.stream()
                    .map(e -> {

                        MbdBalanceRateDataPhaseResVo r = new MbdBalanceRateDataPhaseResVo();
                        if ((phaseIdMap == null || (phaseIdMap.get(e.getId()) != null)) && !"PP".equals(e.getPhaseCode())) {
                            r.setPhaseName(e.getPhaseName());
                            r.setPhaseCode(e.getPhaseCode());
                            BigDecimal totalSecond = new BigDecimal(e.getWorkTime()).multiply(orderBom.getTotalCount()).multiply(new BigDecimal(order.getOrderQty()));
                            r.setWorkTimeMinutes(totalSecond.divide(new BigDecimal(60), 2, RoundingMode.HALF_UP));
                            r.setWorkAbility(stationCount.get(e.getWorkGroupNumber()).get(0).getWorkAbility());
                            r.setWorkGroupNumber(e.getWorkGroupNumber());

                            r.setStationCount(BigDecimal.valueOf(stationCount.get(e.getWorkGroupNumber()).size()));
                            if (proPhaseResVoList != null) { // 重写自定义的工位数
                                for (MbdBalanceRateDataPhaseResVo proR : proPhaseResVoList) {
                                    if (Objects.equals(r.getPhaseCode(), proR.getPhaseCode())) {
                                        r.setWorkTimeMinutes(proR.getWorkTimeMinutes());
                                        r.setWorkAbility(proR.getWorkAbility());
                                        r.setStationCount(proR.getStationCount());
                                        break;
                                    }
                                }
                            }

                            r.setStationTotal(r.getWorkAbility().multiply(r.getStationCount()));
                            r.setProcessRhythm(r.getWorkTimeMinutes().divide(r.getStationTotal(), 2, RoundingMode.HALF_UP));
                            r.setHidden(false);
                            r.setWeldingWireCount(calculateWeldingWireCount(r, order, orderBom, orderPhaseMap, wires, weldingWireOmitted));
                        } else {
                            r.setHidden(true);
                        }
                        return r;
                    })
                    .collect(Collectors.toList()));

        }

        if (pro == null) { // 工位数未定时，才重计算
            reassign(resVo.getPhase(), order);
        }

        // 总工时
        resVo.setTotalWorkingHours(resVo.getPhase().values().stream().flatMap(e -> e.stream().filter(e1 -> !e1.isHidden())).map(MbdBalanceRateDataPhaseResVo::getWorkTimeMinutes).reduce(BigDecimal.ZERO, BigDecimal::add));

        // 总工位数
        BigDecimal totalStationCount = resVo.getPhase().values().stream().flatMap(e -> e.stream().filter(e1 -> !e1.isHidden())).map(MbdBalanceRateDataPhaseResVo::getStationTotal).reduce(BigDecimal.ZERO, BigDecimal::add);
        resVo.setTotalStationCount(totalStationCount);

        // 瓶颈工序节拍
        String maxProcessStage = null;
        MbdBalanceRateDataPhaseResVo maxProcessRhythm = null;
        for (Map.Entry<String, List<MbdBalanceRateDataPhaseResVo>> stagePhases : resVo.getPhase().entrySet()) {
            for (MbdBalanceRateDataPhaseResVo vo : stagePhases.getValue()) {
                if (vo.isHidden()) {
                    continue;
                }
                if (maxProcessRhythm == null || maxProcessRhythm.getProcessRhythm().compareTo(vo.getProcessRhythm()) < 0) {
                    maxProcessRhythm = vo;
                    maxProcessStage = stagePhases.getKey();
                }
            }
        }
        if (maxProcessRhythm == null) {
            throw SyExceptionUtils.e("没有瓶颈工序");
        }
        resVo.setBottleneckProcess(maxProcessRhythm.getProcessRhythm());
        resVo.setBottleneckProcessStage(maxProcessStage);
        resVo.setBottleneckProcessName(maxProcessRhythm.getPhaseName());

        // 平衡率=总工时/（工位数量*瓶颈工序节拍）
        resVo.setBalanceRate(resVo.getTotalWorkingHours().divide(totalStationCount.multiply(maxProcessRhythm.getProcessRhythm()), 4, RoundingMode.HALF_UP));
        resVo.setEquilibriumLossRate(new BigDecimal(1).subtract(resVo.getBalanceRate()));

        // 调整策略
        calculateStrategy(order, resVo.getPhase(), maxProcessRhythm);

        return resVo;
    }

    private void calculateStrategy(Order order, Map<String, List<MbdBalanceRateDataPhaseResVo>> phase, MbdBalanceRateDataPhaseResVo maxProcessRhythm) {
        String pn = "";
        String phaseCode;
        String mater;
        for (Map.Entry<String, List<MbdBalanceRateDataPhaseResVo>> entry : phase.entrySet()) {
            if (entry.getValue().contains(maxProcessRhythm)) {
                pn = entry.getKey();
            }
        }
        phaseCode = maxProcessRhythm.getPhaseCode();
        mater = order.getMaterCode().substring(order.getMaterCode().length() - 6, order.getMaterCode().length() - 2);
        List<DictData> strategy = dictDataMapper.findByDictTypeCode(Wrappers.<DictData>query().eq("dt.dict_code", "orderPhaseBalancingRateStrategy"));
        String dictKey = String.join("|", pn, phaseCode, mater);
        for (DictData dictData : strategy) {
            if (dictData.getDictLabel().equals(dictKey)) {
                maxProcessRhythm.setStrategy(dictData.getDictValue());
                break;
            }
        }
    }

    private MbdBalanceRateDataPhaseResVo initialPhaseResVo(MbdBalanceRateDataPhaseResVo config, Map<String, List<Station>> stationCount, List<MbdBalanceRateDataPhaseResVo> proStationCount, Order order, OrderBom orderBom, Map<Integer, List<OrderPhase>> orderPhaseMap, Map<Integer, Map<Integer, OrderAccessories>> wires, List<DictData> weldingWireOmitted) {
        MbdBalanceRateDataPhaseResVo r = new MbdBalanceRateDataPhaseResVo();
        if (!config.isHidden()) {
            r.setPhaseName(config.getPhaseName());
            r.setPhaseCode(config.getPhaseCode());
            r.setWorkTimeMinutes(config.getWorkTimeMinutes());
            r.setWorkAbility(config.getWorkAbility());
            r.setWorkGroupNumber(config.getWorkGroupNumber());

            r.setStationCount(BigDecimal.valueOf(stationCount.get(config.getWorkGroupNumber()).size()));
            r.setStationCountSetup(config.getStationCountSetup());
            if (proStationCount != null) { // 重写自定义的工位数
                for (MbdBalanceRateDataPhaseResVo proR : proStationCount) {
                    if (Objects.equals(r.getPhaseCode(), proR.getPhaseCode())) {
                        r.setStationCount(proR.getStationCount());
                        break;
                    }
                }
            }
            r.setStationTotal(r.getWorkAbility().multiply(r.getStationCount()));
            r.setProcessRhythm(r.getWorkTimeMinutes().divide(r.getStationTotal(), 2, RoundingMode.HALF_UP));
            r.setHidden(false);

            r.setWeldingWireCount(calculateWeldingWireCount(r, order, orderBom, orderPhaseMap, wires, weldingWireOmitted));
        } else {
            r.setHidden(true);
        }
        return r;
    }

    private static BigDecimal calculateWeldingWireCount(MbdBalanceRateDataPhaseResVo r, Order order, OrderBom orderBom, Map<Integer, List<OrderPhase>> orderPhaseMap, Map<Integer, Map<Integer, OrderAccessories>> wires, List<DictData> weldingWireOmitted) {
        // 排除掉无焊丝的工序
        if (weldingWireOmitted.stream().anyMatch(e -> e.getDictValue().equals(r.getPhaseCode()))) {
            return null;
        }
        // 计算单件焊丝量
        OrderAccessories wire = null;
        OrderPhase phase = null;
        List<OrderPhase> bomPhases = orderPhaseMap.get(orderBom.getId());
        Map<Integer, OrderAccessories> bomWires = wires.get(orderBom.getId());
        if (bomPhases != null) {
            for (OrderPhase bomPhase : bomPhases) {
                if (Objects.equals(bomPhase.getPhaseCode(), r.getPhaseCode())) {
                    if (bomWires != null) {
                        wire = bomWires.get(bomPhase.getId());
                        phase = bomPhase;
                    }
                }
            }
        }
        // 如果当前bom该工序有焊丝信息，则基于基础值进行计算
        if (wire != null && wire.getPartCount() != null && wire.getPartCount().compareTo(BigDecimal.ZERO) > 0) {
            // 原焊丝量
            BigDecimal originalWireCount = wire.getPartCount().multiply(orderBom.getTotalCount()).multiply(new BigDecimal(order.getOrderQty()));
            // 原工时
            BigDecimal originalWorkTime = new BigDecimal(phase.getWorkTime()).multiply(orderBom.getTotalCount()).multiply(new BigDecimal(order.getOrderQty())).divide(new BigDecimal(60), 2, RoundingMode.HALF_UP);
            // 当前焊丝量 = 原焊丝量 + （当前工时 - 原工时）/24.4 * 2.0333333
            return originalWireCount.add(r.getWorkTimeMinutes().subtract(originalWorkTime).multiply(new BigDecimal("2.0333333")).divide(new BigDecimal("24.4"), 2, RoundingMode.HALF_UP));
        }
        // 无基础值的焊丝量估值：当前工时 /24.4 * 2.0333333
        return r.getWorkTimeMinutes().multiply(new BigDecimal("2.0333333")).divide(new BigDecimal("24.4"), 2, RoundingMode.HALF_UP);
    }

    private void reassign(Map<String, List<MbdBalanceRateDataPhaseResVo>> resPhaseMap, Order order) {
        // 根据是否共用工位，将 bom 工序进行分组
        Map<String, List<MbdBalanceRateDataPhaseResVo>> groupedPhase = new HashMap<>();
        resPhaseMap.forEach((key, resPhase) -> {
            resPhase.forEach(phaseResVo -> {
                if (!phaseResVo.isHidden()) {
                    groupedPhase.computeIfAbsent(phaseResVo.getWorkGroupNumber(), gk -> new ArrayList<>()).add(phaseResVo);
                }
            });
        });
        if (groupedPhase.isEmpty()) {
            return;
        }
        // 针对每个组工位，计算最大工序节拍最小的方案
        Map<String, BigDecimal> stationGroupBalancingRateMap = new HashMap<>();
        List<DictData> stationGroupBalancingRate = dictDataMapper.findByDictTypeCode(Wrappers.<DictData>query().eq("dt.dict_code", "stationGroupBalancingRate"));
        for (DictData dictData : stationGroupBalancingRate) {
            String[] keys = dictData.getDictLabel().split("\\|");
            if (!order.getMaterName().contains(keys[1])) {
                continue;
            }
            stationGroupBalancingRateMap.put(keys[0], new BigDecimal(dictData.getDictValue()));
        }
        Map<String, BigDecimal> stationGroupBalancingBaseUnitMap = new HashMap<>();
        List<DictData> stationGroupBalancingBaseUnit = dictDataMapper.findByDictTypeCode(Wrappers.<DictData>query().eq("dt.dict_code", "stationGroupBalancingBaseUnit"));
        for (DictData dictData : stationGroupBalancingBaseUnit) {
            stationGroupBalancingBaseUnitMap.put(dictData.getDictLabel(), new BigDecimal(dictData.getDictValue()));
        }
        for (Map.Entry<String, List<MbdBalanceRateDataPhaseResVo>> entry : groupedPhase.entrySet()) {
            String workGroupNumber = entry.getKey();
            BigDecimal rate = stationGroupBalancingRateMap.getOrDefault(workGroupNumber, BigDecimal.ONE);
            BigDecimal baseUnit = stationGroupBalancingBaseUnitMap.getOrDefault(workGroupNumber, new BigDecimal("0.1"));
            List<MbdBalanceRateDataPhaseResVo> resPhaseList = entry.getValue();
            // 总方案数为 C(stationCount - 1, phaseCount - 1)
            int phaseCount = resPhaseList.size();
            int stationCount = resPhaseList.get(0).getStationCount().divideToIntegralValue(baseUnit).intValue();
            Assert.isTrue(phaseCount <= stationCount, String.format("工位数量不足，无法满足每个工序至少分配一个工位的要求,workGroupNumber=%s,count=%d,phases=%s", workGroupNumber, stationCount, JsonUtils.toJsonString(resPhaseList)));
            List<Integer> scheme = resPhaseList.stream().map(ele -> 1).collect(Collectors.toList());
            int sum = phaseCount;
            BigDecimal minRhythm = BigDecimal.valueOf(Integer.MAX_VALUE);
            List<Integer> minScheme = new ArrayList<>();
            do {
                // 调整最后一个变量值使整个值等于工位数
                scheme.set(scheme.size() - 1, stationCount - sum + scheme.get(scheme.size() - 1));
                sum = stationCount;
                BigDecimal schemeMaxRhythm = calculateMaxRhythm(scheme, resPhaseList);
                if (minRhythm.compareTo(schemeMaxRhythm) >= 0) {
                    minScheme = new ArrayList<>(scheme);
                    minRhythm = schemeMaxRhythm;
                }
                // 增加最后一个可增加的变量值，从而更新方案
                ArrayList<Integer> preScheme = new ArrayList<>(scheme);
                for (int index = phaseCount - 1; index > 0; index--) {
                    Integer value = scheme.get(index);
                    Integer higherValue = scheme.get(index - 1);
                    if (value != 1) {
                        scheme.set(index, 1);
                        scheme.set(index - 1, higherValue + 1);
                        sum -= (value - 2);
                        break;
                    }
                }
                if (preScheme.equals(scheme)) {
                    break;
                }
            } while (sum <= stationCount);
            // 根据最小节拍时的工位分配方案，设置每个工序的工位数、工序节拍
            for (int index = 0; index < phaseCount; index++) {
                // 计算工位总数
                MbdBalanceRateDataPhaseResVo phase = resPhaseList.get(index);
                phase.setStationCount(rate.multiply(BigDecimal.valueOf(minScheme.get(index)).multiply(baseUnit)));
                if (phase.getStationCountSetup() != null && phase.getStationCountSetup().compareTo(BigDecimal.ZERO) > 0) {
                    phase.setStationCount(phase.getStationCountSetup());
                }
                phase.setStationTotal(phase.getWorkAbility().multiply(phase.getStationCount()));
                // 计算工序节拍
                phase.setProcessRhythm(phase.getWorkTimeMinutes().divide(phase.getStationTotal(), 2, RoundingMode.HALF_UP));
            }
        }
    }

    private BigDecimal calculateMaxRhythm(List<Integer> scheme, List<MbdBalanceRateDataPhaseResVo> phaseList) {
        BigDecimal maxRhythm = BigDecimal.ZERO;
        for (int index = 0; index < phaseList.size(); index++) {
            // 计算工位总数
            MbdBalanceRateDataPhaseResVo phase = phaseList.get(index);
            int phaseStationCount = scheme.get(index);
            BigDecimal stationTotal = phase.getWorkAbility().multiply(new BigDecimal(phaseStationCount));
            // 计算工序节拍
            BigDecimal rhythm = phase.getWorkTimeMinutes().divide(stationTotal, 2, RoundingMode.HALF_UP);
            if (rhythm.compareTo(maxRhythm) > 0) {
                maxRhythm = rhythm;
            }
        }
        return maxRhythm;
    }


    private Map<String, List<Station>> getStationCount(List<OrderPhase> orderPhaseList) {
        List<String> workGroupNumber = orderPhaseList.stream().map(OrderPhase::getWorkGroupNumber).distinct().collect(Collectors.toList());

        List<Group> groups = groupMapper.selectList(Wrappers.<Group>lambdaQuery().in(Group::getMyCode, workGroupNumber));
        Map<String, Group> gMap = groups.stream().collect(Collectors.toMap(Group::getMyCode, Function.identity()));

        List<Integer> groupIds = groups.stream().map(Group::getId).collect(Collectors.toList());

//        List<StationGroup> stationGroupList = stationGroupMapper.selectList(Wrappers.<StationGroup>lambdaQuery().in(StationGroup::getGroupId, groupIds).eq(StationGroup::getDefaultGroup, 1));
//        Map<Integer, List<StationGroup>> sgMap = stationGroupList.stream().collect(Collectors.groupingBy(StationGroup::getGroupId));

//        List<Station> stationList = stationMapper.selectList(Wrappers.<Station>lambdaQuery().in(Station::getId, stationGroupList.stream().map(StationGroup::getStationId).distinct().collect(Collectors.toList())));
        List<Station> stationList = stationMapper.lambdaQuery().in(Station::getGroupId, groupIds).list();
        Map<Integer, Station> sMap = stationList.stream().collect(Collectors.toMap(Station::getId, Function.identity()));

        Map<Integer, List<Station>> sgMap = stationList.stream().collect(Collectors.groupingBy(Station::getGroupId));

        List<Shift> shiftList = shiftMapper.lambdaQuery().list();
        for (Station station : stationList) {
            List<Shift> shifts = shiftList.stream().filter(shift -> shift.getShiftTypeId().equals(station.getShiftTypeId())).collect(Collectors.toList());
            // shifts （总有效时长 / 总工作时间 ）
            BigDecimal validTimeResult = shifts.stream().map(e -> new BigDecimal(e.getValidTime())).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal workTimeResult = shifts.stream().map(e -> new BigDecimal(e.getTotalWorkTime())).reduce(BigDecimal.ZERO, BigDecimal::add);
            station.setWorkAbility(validTimeResult.divide(workTimeResult, 2, RoundingMode.HALF_UP));
        }


        Map<String, List<Station>> dataMap = new HashMap<>();
        for (String key : workGroupNumber) {

            Group group = gMap.get(key);
            if (group == null) {
                throw SyExceptionUtils.e("工作组" + key + "不存在");
            }

            List<Station> sgList = sgMap.get(group.getId());
            if (CollUtil.isEmpty(sgList)) {
                throw SyExceptionUtils.e("工作组" + key + "没有工位");
            }


            dataMap.computeIfAbsent(key, k -> new ArrayList<>())
                    .addAll(
                            sgList.stream()
                                    .map(Station::getId)
                                    .distinct()
                                    .map(sMap::get)
                                    .collect(Collectors.toList())
                    );

        }

        return dataMap;

    }

    public MbdGetBalanceRateConfigResVo getBalanceConfig(BaseIdReqVo reqVo) {
        Order order = orderMapper.selectOneAll(reqVo.getId());

        MbdGetBalanceRateConfigResVo resVo = new MbdGetBalanceRateConfigResVo();

        MbdBalanceRate mbdBalanceRate = mbdBalanceRateMapper.selectOne(Wrappers.<MbdBalanceRate>lambdaQuery().eq(MbdBalanceRate::getOrderId, reqVo.getId()));
        resVo.setName(getBalanceRateName(mbdBalanceRate));
        resVo.setMixedOrderIds(getMixedOrderIds(order, mbdBalanceRate));

        List<MbdBalanceRateItem> mbdBalanceRateItems = mbdBalanceRateItemMapper.selectList(Wrappers.<MbdBalanceRateItem>lambdaQuery().eq(MbdBalanceRateItem::getOrderId, reqVo.getId()));
        Map<Integer, MbdBalanceRateItem> itemIdMap = mbdBalanceRateItems.stream()
                .filter(item -> item.getDimensionType() == null || item.getDimensionType() == 1)
                .collect(Collectors.toMap(MbdBalanceRateItem::getPhaseId, Function.identity()));

        String pattern = "^(?<prefix>.+)" + WorkshopProp.PH_PATTERN+"$";
        if (!order.getMaterCode().matches(pattern)) {
            return resVo;
        }

        List<OrderBom> orderBomList = orderBomMapper.listByQuery(Wrappers.query().eq("order_id", order.getId()));
        List<OrderPhase> orderPhaseList = orderPhaseMapper.listByQuery(Wrappers.query().eq("order_id", order.getId()));


        Map<Integer, List<OrderPhase>> orderPhaseMap = orderPhaseList.stream().collect(Collectors.groupingBy(OrderPhase::getBomId));

        String preFix = order.getMaterCode().substring(0, order.getMaterCode().length() - 2);
        String patternStr = preFix + WorkshopProp.PH_PATTERN;
        List<OrderBom> collect = orderBomList.stream().filter(e -> e.getMaterCode().matches(patternStr)).sorted(Comparator.comparing(OrderBom::getLevel)).collect(Collectors.toList());
        for (OrderBom orderBom : collect) {
            String simpleNo = orderBom.getMaterCode().replaceFirst(preFix, "");
            MbdGetBalanceRateConfigDataResVo dataResVo = new MbdGetBalanceRateConfigDataResVo();
            dataResVo.setName(simpleNo);
            resVo.getData().add(dataResVo);

            List<OrderPhase> orderPhaseList1 = orderPhaseMap.get(orderBom.getId());
            if (CollUtil.isEmpty(orderPhaseList1)) {
                throw SyExceptionUtils.e("物料" + orderBom.getMaterCode() + "没有工序");
            }
            orderPhaseList1.sort(Comparator.comparing(OrderPhase::getPhaseSeq));

            dataResVo.setChecked(
                    orderPhaseList1.stream()
                            .map(e -> {
                                MbdGetBalanceRateConfigDataCheckedResVo phaseResVo = new MbdGetBalanceRateConfigDataCheckedResVo();
                                phaseResVo.setId(e.getId());
                                phaseResVo.setText(e.getPhaseName());
                                phaseResVo.setChecked(calculateCheckStatus(itemIdMap, e));
                                return phaseResVo;
                            })
                            .collect(Collectors.toList())
            );
        }

        return resVo;
    }

    private List<Integer> getMixedOrderIds(Order order, MbdBalanceRate mbdBalanceRate) {
        if (mbdBalanceRate != null && StringUtils.isNotBlank(mbdBalanceRate.getMixedOrderIds())) {
            return Arrays.stream(mbdBalanceRate.getMixedOrderIds().split(",")).map(Integer::parseInt).collect(Collectors.toList());
        }
        return Collections.singletonList(order.getId());
    }

    private String getBalanceRateName(MbdBalanceRate config) {
        if (config != null && StringUtils.isNotBlank(config.getName())) {
            return config.getName();
        }
        return "系统智能推荐待平衡工位";
    }

    private boolean calculateCheckStatus(Map<Integer, MbdBalanceRateItem> itemIdMap, OrderPhase phase) {
        MbdBalanceRateItem mbdBalanceRateItem = itemIdMap.get(phase.getId());
        if (mbdBalanceRateItem != null) {
            return true;
        } else if(itemIdMap.isEmpty() && Arrays.asList("RH","PH","LH","BH").contains(phase.getPhaseCode())){
            mbdBalanceRateItem = new MbdBalanceRateItem();
            mbdBalanceRateItem.setOrderId(phase.getOrderId());
            mbdBalanceRateItem.setPhaseId(phase.getId());
            mbdBalanceRateItem.setDimensionType(1);
            mbdBalanceRateItem.setMbdBalanceRateId(phase.getId());
            mbdBalanceRateItemMapper.insert(mbdBalanceRateItem);
            return true;
        }
        return false;
    }

    @Transactional
    public void addBalanceConfig(MbdAddBalanceRateConfigReqVo reqVo) {

        // 先删除以前的数据
        mbdBalanceRateMapper.deleteByOrderId(reqVo.getOrderId());
        mbdBalanceRateItemMapper.lambdaUpdate()
                .eq(MbdBalanceRateItem::getOrderId, reqVo.getOrderId())
                .eq(MbdBalanceRateItem::getDimensionType, 1)
                .remove();

        // 保存 mbdBalanceRate
        MbdBalanceRate mbdBalanceRate = new MbdBalanceRate();
        mbdBalanceRate.setOrderId(reqVo.getOrderId());
        mbdBalanceRate.setName(reqVo.getName());
        mbdBalanceRate.setMixedOrderIds(reqVo.getMixedOrderIds().stream().map(String::valueOf).collect(Collectors.joining(",")));
        mbdBalanceRateMapper.insert(mbdBalanceRate);
        // 保存 mbdBalanceRateItem
        for (Integer woId : reqVo.getApsWorkOrderId()) {
            MbdBalanceRateItem mbdBalanceRateItem = new MbdBalanceRateItem();
            mbdBalanceRateItem.setOrderId(reqVo.getOrderId());
            mbdBalanceRateItem.setPhaseId(woId);
            mbdBalanceRateItem.setDimensionType(1);
            mbdBalanceRateItem.setMbdBalanceRateId(woId);
            mbdBalanceRateItemMapper.insert(mbdBalanceRateItem);
        }

    }

    @Transactional
    public void nestSync() {
        // 同步数据
        BaseListResDto<MesDxfFindResDto> res = momSrvMbdApiClient.mesDxfFind();
        log.info("mbd-同步-响应：{}", JsonUtils.toJsonString(res));

        if (!res.getCode().equals(0)) {
            throw SyExceptionUtils.e("mbd套料图同步失败!");
        }

        if (CollUtil.isEmpty(res.getData())) {
            return;
        }

        // 本地数据库保存
        List<ImportMbdDxf> dxfList = res.getData()
                .stream()
                .map(e -> {
                    ImportMbdDxf dxf = new ImportMbdDxf();
                    BeanUtils.copyProperties(e, dxf, "createTime");
                    dxf.setCreateTime(DateUtil.toLocalDateTime(e.getCreateTime()));
                    return dxf;
                })
                .collect(Collectors.toList());
        importMbdDxfMapper.insertBatchSomeColumn(dxfList);

        // 找到未完成的订单，找到所有的含有此plmId的orderbom数据，并且更新
        List<String> plmIds = dxfList.stream().distinct().map(ImportMbdDxf::getPlmid).collect(Collectors.toList());
        List<OrderBom> orderBomList = orderBomMapper.selectMbdDxf(Wrappers.<OrderBom>query()
                .in("ob.plm_id", plmIds)
                .eq("o.order_type", 2));

        Map<String, ImportMbdDxf> collect1 = dxfList.stream().collect(Collectors.toMap(ImportMbdDxf::getPlmid, Function.identity(), (e1, e2) -> {
            if (e1.getCreateTime().isAfter(e2.getCreateTime())) {
                return e1;
            }
            return e2;
        }));

        for (OrderBom bom : orderBomList) {
            String path = collect1.get(bom.getPlmId()).getPath();

            if (path.equals(bom.getDrawingPath())) {
                continue;
            }

            LambdaUpdateWrapper<OrderBom> wrapper = Wrappers.lambdaUpdate();
            wrapper.eq(OrderBom::getId, bom.getId());
            wrapper.set(OrderBom::getDrawingPath, path);
            orderBomMapper.update(null, wrapper);

            // 调用修改套料接口
            Order order = orderMapper.selectOneAll(bom.getOrderId());

            SinoCam sinoCam = sinoCamMapper.selectOne(
                    Wrappers.<SinoCam>lambdaQuery()
                            .eq(SinoCam::getOrderId, order.getId())
                            .eq(SinoCam::getType, 1)
                            .orderByDesc(SinoCam::getModifyDateTime)
            );

            if (sinoCam == null) {
                continue;
            }

            LambdaQueryWrapper<SinoCamPushDrawing> query = Wrappers.lambdaQuery();
            query.eq(SinoCamPushDrawing::getSinoCamId, sinoCam.getId());
            query.eq(SinoCamPushDrawing::getChartId, bom.getPlmId());
            List<SinoCamPushDrawing> list = sinoCamPushDrawingMapper.selectList(query);

            for (SinoCamPushDrawing sinoCamPushDrawing : list) {
                sinoCamPushDrawing.setState("C");
//                String versionBase = sinoCamPushDrawing.getVersionBase();
//                char c = (char) (versionBase.charAt(0) + 1);
//                sinoCamPushDrawing.setVersionBase(String.valueOf(c));
                sinoCamPushDrawing.setFilePath(path);
                sinoCamPushDrawingMapper.updateById(sinoCamPushDrawing);
            }

            try {
                List<SinoCamPushLoftReqDto> reqDtoList = list.stream()
                        .map(e -> {
                            SinoCamPushLoftReqDto rr = new SinoCamPushLoftReqDto();
                            BeanUtils.copyProperties(e, rr);
                            rr.setFactoryId(e.getFactoryIdk());
                            return rr;
                        })
                        .collect(Collectors.toList());
                log.info(JsonUtils.toJsonString(list));
                SinoCamCommonsResDto sinoCamCommonsResDto = sinoCamApiClient.pushNestLofting(reqDtoList);
                log.info(JsonUtils.toJsonString(sinoCamCommonsResDto));
                if (sinoCamCommonsResDto.getResult() != null && sinoCamCommonsResDto.getResult().equals("S")) {
                    sinoCam.setResult(3);
                } else {
                    sinoCam.setResult(4);
                }
                sinoCam.setMessage(sinoCamCommonsResDto.getMessage());
            } catch (Exception e) {
                log.error(e.getMessage(), e);
                sinoCam.setResult(4);
                sinoCam.setMessage(e.getMessage());
            }


        }


        // 同步确认
        List<MesDxfConfirmDataReqDto> collect = res.getData()
                .stream()
                .map(e -> MkBeanUtils.copyProperties(e, MesDxfConfirmDataReqDto.class))
                .collect(Collectors.toList());
        MesDxfConfirmReqDto reqDto = new MesDxfConfirmReqDto();
        reqDto.setData(collect);
        log.info("mbd-同步确认-请求：{}", JsonUtils.toJsonString(reqDto));
//        BaseResDto baseResDto = momSrvMbdApiClient.mesDxfConfirm(reqDto);
//        log.info("mbd-同步确认-响应：{}", JsonUtils.toJsonString(baseResDto));

    }

    public List<MbdBalanceRateDataListResVo> listBalanceRateData(MbdBalanceRateDataListReqVo reqVo) {
        return mbdBalanceRateDataMapper.lambdaQuery()
                .select(MbdBalanceRateData::getId, MbdBalanceRateData::getCreateDateTime)
                .eq(MbdBalanceRateData::getOrderId, reqVo.getId())
                .eq(reqVo.getDimensionType() > 0, MbdBalanceRateData::getDimensionType, reqVo.getDimensionType())
                .list()
                .stream()
                .sorted(Comparator.comparing(MbdBalanceRateData::getCreateDateTime).reversed())
                .map(entity -> {
                    MbdBalanceRateDataListResVo itemVo = new MbdBalanceRateDataListResVo();
                    itemVo.setId(entity.getId());
                    itemVo.setSaveTime(entity.getCreateDateTime().format(DateTimeFormatter.ofPattern(" yyyy-MM-dd HH:mm:ss")));
                    return itemVo;
                })
                .collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    public void saveBalanceRateData(MbdBalanceRateDataSaveReqVo reqVo) {
        MbdBalanceRateData exist = null;
        if (reqVo.getId() != null) {
            exist = mbdBalanceRateDataMapper.selectById(reqVo.getId());
            if (exist == null) {
                throw SyExceptionUtils.e("留痕记录不存在" + reqVo.getId());
            }
        }
        MbdBalanceRateData entity = exist == null ? new MbdBalanceRateData() : exist;
        entity.setName(reqVo.getName());
        entity.setOrderId(reqVo.getOrderId());
        entity.setOrderQty(reqVo.getOrderQty());
        entity.setDataPro(JsonUtils.toJsonString(reqVo.getPro()));
        entity.setDataCustomize(JsonUtils.toJsonString(reqVo.getCustomize()));
        entity.setDimensionType(1);
        mbdBalanceRateDataMapper.insertOrUpdate(entity);
        // 关联操作记录
        mbdBalanceRateOpMapper.lambdaUpdate()
                .eq(MbdBalanceRateOp::getOrderId, reqVo.getOrderId())
                .eq(MbdBalanceRateOp::getDimensionType, 1)
                .isNull(MbdBalanceRateOp::getDataId)
                .set(MbdBalanceRateOp::getDataId, entity.getId())
                .update();
    }

    public void deleteBalanceRateData(BaseIdReqVo reqVo) {
        mbdBalanceRateDataMapper.deleteById(reqVo.getId());
    }

    public List<MbdBalanceRateDataComparisonResVo> balanceRateComparison(MbdBalanceRateDataComparisonReqVo reqVo) {
        return mbdBalanceRateDataMapper.lambdaQuery()
                .in(MbdBalanceRateData::getId, reqVo.getIds())
                .orderByAsc(MbdBalanceRateData::getCreateDateTime)
                .list().stream()
                .map(balanceRateData -> {
                    try {
                        balanceRateData.setPro(objectMapper.readValue(balanceRateData.getDataPro(), MbdBalanceRateDataResVo.class));
                    } catch (JsonProcessingException e) {
                        throw SyExceptionUtils.e("数据异常");
                    }
                    MbdBalanceRateDataComparisonResVo resVo = new MbdBalanceRateDataComparisonResVo();
                    BeanUtils.copyProperties(balanceRateData.getPro(), resVo);
                    resVo.setId(balanceRateData.getId());
                    resVo.setSaveTime(balanceRateData.getCreateDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                    resVo.setOrderId(balanceRateData.getOrderId());
                    return resVo;
                })
                .collect(Collectors.toList());
    }

    public List<MbdBalancePhaseListResVo> listBalanceRatePhase(MbdBalancePhaseListReqVo reqVo) {
        Order order = orderMapper.selectOneAll(reqVo.getId());
        String pattern = "^(?<prefix>.+)" + WorkshopProp.PH_PATTERN+"$";
        if (!order.getMaterCode().matches(pattern)) {
            throw SyExceptionUtils.e("只有P*结尾的物料才能计算生产平衡率");
        }
        String preFix = order.getMaterCode().substring(0, order.getMaterCode().length() - 2);
        List<OrderBom> orderBomList = orderBomMapper.listByQuery(Wrappers.query().eq("order_id", order.getId()));
        Map<Integer, List<OrderPhase>> orderPhaseMap = orderPhaseMapper
                .listByQuery(Wrappers.query().eq("order_id", order.getId()))
                .stream()
                .collect(Collectors.groupingBy(OrderPhase::getBomId));
        List<MbdBalancePhaseListResVo> result = new ArrayList<>();
        Set<String> keys = new HashSet<>();


        String patternStr = preFix + WorkshopProp.PH_PATTERN;
        List<OrderBom> collect = orderBomList.stream().filter(e -> e.getMaterCode().matches(patternStr)).sorted(Comparator.comparing(OrderBom::getLevel)).collect(Collectors.toList());
        for (OrderBom orderBom : collect) {
//            String simpleNo = orderBom.getMaterCode().replaceFirst(preFix, "");
            for (OrderPhase orderPhase : orderPhaseMap.get(orderBom.getId())) {
                String key = String.join("_", orderPhase.getPhaseCode(), orderPhase.getPhaseName(), orderPhase.getWorkGroupNumber());
                if (keys.add(key)) { // 去重
                    MbdBalancePhaseListResVo item = new MbdBalancePhaseListResVo();
                    item.setPhaseCode(orderPhase.getPhaseCode());
                    item.setPhaseName(orderPhase.getPhaseName());
                    item.setWorkGroupNumber(orderPhase.getWorkGroupNumber());
                    result.add(item);
                }
            }
        }

        return result;
    }

    public Workbook exportBalanceRateComparison(MbdBalanceRateDataComparisonReqVo reqVo) {
        List<MbdBalanceRateDataComparisonResVo> comparisonResVos = balanceRateComparison(reqVo);
        Assert.notEmpty(comparisonResVos, "没有数据可导出");
        // 过滤掉隐藏数据
        for (MbdBalanceRateDataComparisonResVo comparison : comparisonResVos) {
            Map<String, List<MbdBalanceRateDataPhaseResVo>> phases = new LinkedHashMap<>();
            comparison.getPhase().forEach((key, value) -> phases.put(key, value.stream().filter(e -> !e.isHidden()).collect(Collectors.toList())));
            comparison.setPhase(phases);
        }

        Integer orderId = comparisonResVos.get(0).getOrderId();
        Order order = orderMapper.selectOneAll(orderId);
        String preFix = order.getMaterCode().substring(0, order.getMaterCode().length() - 2);
        String patternStr = preFix + WorkshopProp.PH_PATTERN;
        Map<Integer, OrderBom> orderBomMap = orderBomMapper.listByQuery(Wrappers.query().eq("order_id", orderId))
                .stream()
                .filter(e -> e.getMaterCode().matches(patternStr))
                .collect(Collectors.toMap(OrderBom::getId, Function.identity()));
        Map<String, Map<String, OrderPhase>> orderPhaseMap = orderPhaseMapper.listByQuery(Wrappers.query().eq("order_id", order.getId()))
                .stream()
                .filter(phase -> orderBomMap.containsKey(phase.getBomId()))
                .sorted(Comparator.comparing(phase -> orderBomMap.get(phase.getBomId()).getLevel()))
                .collect(Collectors.groupingBy(
                        (OrderPhase phase) -> orderBomMap.get(phase.getBomId()).getMaterCode().replaceFirst(preFix, ""),
                        LinkedHashMap::new,
                        Collectors.toMap(OrderPhase::getPhaseCode, Function.identity(), (e1, e2) -> e1)
                ));
        Map<Integer, List<MbdBalanceRateOp>> operations = mbdBalanceRateOpMapper.lambdaQuery()
                .eq(MbdBalanceRateOp::getOrderId, orderId)
                .in(MbdBalanceRateOp::getDataId, comparisonResVos.stream().map(MbdBalanceRateDataComparisonResVo::getId).collect(Collectors.toList()))
                .list().stream()
                .collect(Collectors.groupingBy(MbdBalanceRateOp::getDataId));
        return exportBalanceRateComparison(comparisonResVos, orderPhaseMap, orderBomMap, order, operations);
    }

    private static Workbook exportBalanceRateComparison(List<MbdBalanceRateDataComparisonResVo> comparisonResVos,
                                                        Map<String, Map<String, OrderPhase>> orderPhaseMap, Map<Integer, OrderBom> orderBomMap, Order order,
                                                        Map<Integer, List<MbdBalanceRateOp>> operations) {
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet("生产平衡率明细对比");
        int rowIndex = -1;
        // 头部：工序、时间1、时间2，......，时间n
        createHeaderRowCell(workbook, sheet.createRow(++rowIndex), comparisonResVos);
        // 统计数据 -- 生产平衡率（%）、瓶颈工序（分）、工位总数量（个）、总工时（分）
        createCountRowCell(workbook, sheet.createRow(++rowIndex), "生产平衡率（%）", comparison -> comparison.getBalanceRate().multiply(BigDecimal.valueOf(100)), comparisonResVos);
        createCountRowCell(workbook, sheet.createRow(++rowIndex), "瓶颈工序（分）", MbdBalanceRateDataComparisonResVo::getBottleneckProcess, comparisonResVos);
        createCountRowCell(workbook, sheet.createRow(++rowIndex), "工位总数量（个）", MbdBalanceRateDataComparisonResVo::getTotalStationCount, comparisonResVos);
        createCountRowCell(workbook, sheet.createRow(++rowIndex), "总工时（分）", MbdBalanceRateDataComparisonResVo::getTotalWorkingHours, comparisonResVos);
        // 工序数据 -- 实动工时（分）、工位数量（个）、制造能力、工序节拍
        for (Map.Entry<String, Map<String, OrderPhase>> entry : orderPhaseMap.entrySet()) {
            String stage = entry.getKey();
            List<Integer> scanIndex = comparisonResVos.stream().map(e -> -1).collect(Collectors.toCollection(ArrayList::new)); // 每个留痕已扫描的行号
            List<MbdBalanceRateDataPhaseResVo> phaseList;
            while ((phaseList = scanComparisonPhase(comparisonResVos, stage, scanIndex)) != null) {
                MbdBalanceRateDataPhaseResVo phase = null;
                for (int index = 0; index < phaseList.size(); index++) {
                    MbdBalanceRateDataPhaseResVo vo = phaseList.get(index);
                    if (vo != null) {
                        phase = vo;
                        scanIndex.set(index, scanIndex.get(index) + 1);
                    }
                }
                if (phase == null) {
                    break;
                }
                OrderPhase basePhase = entry.getValue().get(phase.getPhaseCode());
                BigDecimal baseWorkTime = BigDecimal.ZERO;
                if (basePhase != null) {
                    OrderBom orderBom = orderBomMap.get(basePhase.getBomId());
                    BigDecimal totalSecond = new BigDecimal(basePhase.getWorkTime()).multiply(orderBom.getTotalCount()).multiply(new BigDecimal(order.getOrderQty()));
                    baseWorkTime = totalSecond.divide(new BigDecimal(60), 2, RoundingMode.HALF_UP);
                }
                createPhaseRowCell(workbook, sheet.createRow(++rowIndex), phase, stage, baseWorkTime, phaseList, comparisonResVos, operations);
                createPhaseRowCell(workbook, sheet.createRow(++rowIndex), phase, stage, phaseList, "工位数量（个）", MbdBalanceRateDataPhaseResVo::getStationCount);
                createPhaseRowCell(workbook, sheet.createRow(++rowIndex), phase, stage, phaseList, "制造能力", MbdBalanceRateDataPhaseResVo::getWorkAbility);
                createPhaseRowCell(workbook, sheet.createRow(++rowIndex), phase, stage, phaseList, "工序节拍", MbdBalanceRateDataPhaseResVo::getProcessRhythm);
            }
        }
        return workbook;
    }

    private static void createPhaseRowCell(Workbook workbook, Row row, MbdBalanceRateDataPhaseResVo phase, String stage, List<MbdBalanceRateDataPhaseResVo> dataList, String title, Function<MbdBalanceRateDataPhaseResVo, BigDecimal> valueMapper) {
        createTitleCell(workbook, row, String.format("%s_%s_%s_%s", stage, phase.getPhaseName(), phase.getPhaseCode(), title));
        BigDecimal baseValue = dataList.get(0) != null ? valueMapper.apply(dataList.get(0)) : BigDecimal.ZERO;
        for (int i = 0; i < dataList.size(); i++) {
            Cell cell = row.createCell(i + 1);
            CellStyle cellStyle = workbook.createCellStyle();
            cellStyle.setAlignment(HorizontalAlignment.CENTER);
            MbdBalanceRateDataPhaseResVo vo = dataList.get(i);
            String cellValue = "-";
            if (vo != null) {
                BigDecimal value = valueMapper.apply(vo);
                BigDecimal diff = value.subtract(baseValue);
                cellValue = value.toString();
                if (diff.compareTo(BigDecimal.ZERO) != 0) {
                    cellStyle.setFont(calculateFontColor(workbook, diff));
                    BigDecimal diffPercentage = diff.abs().divide(baseValue, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
                    cellValue = String.format("%s（%s%%）", value, diffPercentage);
                }
            }
            cell.setCellStyle(cellStyle);
            cell.setCellValue(cellValue);
        }
    }

    private static void createPhaseRowCell(Workbook workbook, Row row, MbdBalanceRateDataPhaseResVo phase, String stage, BigDecimal baseValue, List<MbdBalanceRateDataPhaseResVo> dataList,
                                           List<MbdBalanceRateDataComparisonResVo> comparisonResVos, Map<Integer, List<MbdBalanceRateOp>> operations) {
        createTitleCell(workbook, row, String.format("%s_%s_%s_实动工时（分）", stage, phase.getPhaseName(), phase.getPhaseCode()));
        for (int i = 0; i < dataList.size(); i++) {
            Cell cell = row.createCell(i + 1);
            CellStyle cellStyle = workbook.createCellStyle();
            cellStyle.setAlignment(HorizontalAlignment.CENTER);
            MbdBalanceRateDataPhaseResVo vo = dataList.get(i);
            MbdBalanceRateDataComparisonResVo comparison = comparisonResVos.get(i);
            String cellValue = "-";
            if (vo != null) {
                BigDecimal value = vo.getWorkTimeMinutes();
                BigDecimal diff = value.subtract(baseValue);
                cellValue = value.toString();
                if (diff.compareTo(BigDecimal.ZERO) != 0) {
                    cellStyle.setFont(calculateFontColor(workbook, diff));
                    cellValue = formatWorkTimeCell(stage, phase, value, diff, comparison, operations);
                }
            }
            cell.setCellStyle(cellStyle);
            cell.setCellValue(cellValue);
        }
    }

    private static String formatWorkTimeCell(String stage, MbdBalanceRateDataPhaseResVo phase, BigDecimal value, BigDecimal diff, MbdBalanceRateDataComparisonResVo comparison, Map<Integer, List<MbdBalanceRateOp>> operations) {
        if (comparison == null) {
            return String.format("%s（调整%s（%s）工序%s分钟）", value, phase.getPhaseName(), phase.getPhaseCode(), diff.abs());
        }
        List<MbdBalanceRateOp> comparisonOp = operations.get(comparison.getId());
        if (comparisonOp == null) {
            return String.format("%s（调整%s（%s）工序%s分钟）", value, phase.getPhaseName(), phase.getPhaseCode(), diff.abs());
        }
        List<MbdBalanceRateOp> phaseOp = new ArrayList<>();
        for (MbdBalanceRateOp op : comparisonOp) {
            if (op.getChangedStage().equals(stage) && op.getChangedPhaseCode().equals(phase.getPhaseCode())) {
                phaseOp.add(op);
            }
            if (op.getInfluencedStage().equals(stage) && op.getInfluencedPhaseCode().equals(phase.getPhaseCode())) {
                MbdBalanceRateOp influencedOp = new MbdBalanceRateOp();
                influencedOp.setChangedValue(BigDecimal.ZERO.subtract(op.getChangedValue()));
                influencedOp.setInfluencedStage(op.getChangedStage());
                influencedOp.setInfluencedPhaseCode(op.getChangedPhaseCode());
                influencedOp.setInfluencedPhaseName(op.getChangedPhaseName());
                phaseOp.add(influencedOp);
            }
        }
        Map<String, Map<String, List<MbdBalanceRateOp>>> map = phaseOp.stream()
                .collect(Collectors.groupingBy(MbdBalanceRateOp::getInfluencedStage, LinkedHashMap::new,
                        Collectors.groupingBy(MbdBalanceRateOp::getInfluencedPhaseCode, LinkedHashMap::new, Collectors.toList())));
        List<String> fm = new ArrayList<>();
        map.forEach((influencedStage, influencedPhases) -> {
            influencedPhases.forEach((influencedPhaseCode, ops) -> {
                BigDecimal changedValue = ops.stream().map(MbdBalanceRateOp::getChangedValue).reduce(BigDecimal.ZERO, BigDecimal::add);
                fm.add(String.format("调整%s_%s（%s）%s分钟", influencedStage, ops.get(0).getInfluencedPhaseName(), influencedPhaseCode, changedValue));
            });
        });
        return String.format("%s（%s）", value, String.join("；\n", fm));
    }

    private static void createTitleCell(Workbook workbook, Row row, String title) {
        Cell titleCell = row.createCell(0);
        CellStyle titleCellStyle = workbook.createCellStyle();
        titleCellStyle.setAlignment(HorizontalAlignment.LEFT);
        titleCell.setCellStyle(titleCellStyle);
        titleCell.setCellValue(title);
    }

    private static Font calculateFontColor(Workbook workbook, BigDecimal diff) {
        XSSFFont font = (XSSFFont) workbook.createFont();
        byte[] color = diff.compareTo(BigDecimal.ZERO) > 0 ? new byte[]{(byte) 0xfa, (byte) 0xc8, (byte) 0x59}
                : new byte[]{(byte) 0x91, (byte) 0xcb, (byte) 0x74};
        font.setColor(new XSSFColor(color));
        return font;
    }

    private static List<MbdBalanceRateDataPhaseResVo> scanComparisonPhase(List<MbdBalanceRateDataComparisonResVo> comparisonResVos,
                                                                          String stage, List<Integer> scanIndex) {
        // 找出所有比对的当前首个未扫描数据
        List<MbdBalanceRateDataPhaseResVo> result = new ArrayList<>();
        List<List<MbdBalanceRateDataPhaseResVo>> subSequences = new ArrayList<>();
        for (int comparisonIndex = 0; comparisonIndex < scanIndex.size(); comparisonIndex++) {
            MbdBalanceRateDataComparisonResVo comparison = comparisonResVos.get(comparisonIndex);
            List<MbdBalanceRateDataPhaseResVo> snapshotStagePhase = comparison.getPhase().get(stage);
            int comparisonScanIndex = scanIndex.get(comparisonIndex) + 1; // 当前待扫描行号
            if (comparisonScanIndex < snapshotStagePhase.size()) {
                result.add(snapshotStagePhase.get(comparisonScanIndex));
                subSequences.add(snapshotStagePhase.subList(comparisonScanIndex, snapshotStagePhase.size()));
            } else {
                result.add(null);
                subSequences.add(new ArrayList<>());
            }
        }
        // 计算当前行工序
        MbdBalanceRateDataPhaseResVo phase = calculateComparisonPhase(result, subSequences);
        if (phase != null) {
            for (int index = 0; index < result.size(); index++) {
                MbdBalanceRateDataPhaseResVo vo = result.get(index);
                if (vo != null && !vo.getPhaseCode().equals(phase.getPhaseCode())) {
                    // 如果一个比对与当前行工序不同，则移除该比对
                    result.set(index, null);
                }
            }
            return result;
        }
        return null;
    }

    private static MbdBalanceRateDataPhaseResVo calculateComparisonPhase(List<MbdBalanceRateDataPhaseResVo> rowData,
                                                                         List<List<MbdBalanceRateDataPhaseResVo>> subSequences) {
        // 找出首个不是其他比对子工序的比对
        for (int index = 0; index < rowData.size(); index++) {
            MbdBalanceRateDataPhaseResVo vo = rowData.get(index);
            if (vo == null) {
                continue;
            }
            boolean existSupperSequence = false;
            for (int followIndex = index + 1; followIndex < rowData.size(); followIndex++) {
                MbdBalanceRateDataPhaseResVo follow = rowData.get(followIndex);
                if (follow == null || follow.getPhaseCode().equals(vo.getPhaseCode())) {
                    continue;
                }
                if (indexOf(vo, subSequences.get(index), follow, subSequences.get(followIndex)) > -1) {
                    existSupperSequence = true;
                    break;
                }
            }
            if (!existSupperSequence) {
                return vo;
            }
        }
        // 如果互相是子工序，则返回首个
        return rowData.stream().filter(Objects::nonNull).findFirst().orElse(null);
    }

    private static int indexOf(MbdBalanceRateDataPhaseResVo sub, List<MbdBalanceRateDataPhaseResVo> subPhases,
                               MbdBalanceRateDataPhaseResVo phase, List<MbdBalanceRateDataPhaseResVo> phases) {
        for (int index = 0; index < phases.size(); index++) {
            if (phases.get(index).getPhaseCode().equals(sub.getPhaseCode())) {
                return index;
            }
        }
        return -1;
    }

    private static void createHeaderRowCell(Workbook workbook, Row header, List<MbdBalanceRateDataComparisonResVo> dataList) {
        Cell phaseCell = header.createCell(0);
        CellStyle phaseCellStyle = workbook.createCellStyle();
        phaseCellStyle.setAlignment(HorizontalAlignment.LEFT);
        Font headerFont = workbook.createFont();
        headerFont.setBold(true);
        phaseCellStyle.setFont(headerFont);
        phaseCell.setCellStyle(phaseCellStyle);
        phaseCell.setCellValue("工序");
        for (int i = 0; i < dataList.size(); i++) {
            Cell timeCell = header.createCell(i + 1);
            CellStyle timeCellStyle = workbook.createCellStyle();
            timeCellStyle.setAlignment(HorizontalAlignment.CENTER);
            timeCellStyle.setFont(headerFont);
            timeCell.setCellStyle(timeCellStyle);
            timeCell.setCellValue(dataList.get(i).getSaveTime());
        }
    }

    private static <T> void createCountRowCell(Workbook workbook, Row row, String title, Function<T, BigDecimal> valueMapper, List<T> dataList) {
        createTitleCell(workbook, row, title);
        BigDecimal baseValue = valueMapper.apply(dataList.get(0));
        for (int i = 0; i < dataList.size(); i++) {
            Cell cell = row.createCell(i + 1);
            CellStyle cellStyle = workbook.createCellStyle();
            cellStyle.setAlignment(HorizontalAlignment.CENTER);
            BigDecimal value = valueMapper.apply(dataList.get(i));
            BigDecimal diff = value.subtract(baseValue);
            String cellValue = value.toString();
            if (diff.compareTo(BigDecimal.ZERO) != 0) {
                cellStyle.setFont(calculateFontColor(workbook, diff));
                BigDecimal diffPercentage = diff.abs().divide(baseValue, 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100));
                cellValue = String.format("%s（%s%%）", value, diffPercentage);
            }
            cell.setCellStyle(cellStyle);
            cell.setCellValue(cellValue);
        }
    }

    public MbdGetBalanceRateConfigOfGroupResVo getBalanceConfigOfGroup(BaseIdReqVo reqVo) {
        MbdGetBalanceRateConfigOfGroupResVo resVo = new MbdGetBalanceRateConfigOfGroupResVo();

        Order order = orderMapper.selectOneAll(reqVo.getId());
        MbdBalanceRate mbdBalanceRate = mbdBalanceRateMapper.lambdaQuery().eq(MbdBalanceRate::getOrderId, reqVo.getId()).one();
        resVo.setName(getBalanceRateName(mbdBalanceRate));
        resVo.setMixedOrderIds(getMixedOrderIds(order, mbdBalanceRate));

        Map<String, MbdBalanceRateItem> itemIdMap = mbdBalanceRateItemMapper.lambdaQuery()
                .eq(MbdBalanceRateItem::getOrderId, reqVo.getId())
                .eq(MbdBalanceRateItem::getDimensionType, 2)
                .list().stream()
                .collect(Collectors.toMap(MbdBalanceRateItem::getName, Function.identity()));

        List<DictData> phaseWorkGroup = dictDataMapper.findByDictTypeCode("BalanceRatePhaseWorkGroup");
        for (DictData dictData : phaseWorkGroup) {
            MbdGetBalanceRateConfigOfGroupResVo.Data dataResVo = new MbdGetBalanceRateConfigOfGroupResVo.Data();
            dataResVo.setName(dictData.getDictLabel());
            resVo.getData().add(dataResVo);
            dataResVo.setChecked(new ArrayList<>());
            for (String workGroupNumber : dictData.getDictValue().split(",")) {
                MbdGetBalanceRateConfigOfGroupResVo.DataItem phaseResVo = new MbdGetBalanceRateConfigOfGroupResVo.DataItem();
                phaseResVo.setId(workGroupNumber);
                phaseResVo.setText(workGroupNumber);
                phaseResVo.setChecked(CalculateCheckStatusOfGroup(dictData, workGroupNumber, itemIdMap, reqVo.getId()));
                dataResVo.getChecked().add(phaseResVo);
            }
        }

        return resVo;
    }

    private boolean CalculateCheckStatusOfGroup(DictData dictData, String workGroupNumber, Map<String, MbdBalanceRateItem> itemIdMap, Integer orderId) {
        if (itemIdMap.containsKey(workGroupNumber)) {
            return true;
        }
        if (itemIdMap.isEmpty() && dictData.getDictLabel().equals("YM")) {
            MbdBalanceRateItem mbdBalanceRateItem = new MbdBalanceRateItem();
            mbdBalanceRateItem.setOrderId(orderId);
            mbdBalanceRateItem.setName(workGroupNumber);
            mbdBalanceRateItem.setDimensionType(2);
            mbdBalanceRateItemMapper.insert(mbdBalanceRateItem);
            return true;
        }
        return false;
    }

    @Transactional
    public void addBalanceConfigOfGroup(MbdAddBalanceRateConfigOfGroupReqVo reqVo) {
        // 先删除以前的数据
        mbdBalanceRateMapper.deleteByOrderId(reqVo.getOrderId());
        mbdBalanceRateItemMapper.lambdaUpdate()
                .eq(MbdBalanceRateItem::getOrderId, reqVo.getOrderId())
                .eq(MbdBalanceRateItem::getDimensionType, 2)
                .remove();

        // 保存 mbdBalanceRate
        MbdBalanceRate mbdBalanceRate = new MbdBalanceRate();
        mbdBalanceRate.setOrderId(reqVo.getOrderId());
        mbdBalanceRate.setName(reqVo.getName());
        mbdBalanceRate.setMixedOrderIds(reqVo.getMixedOrderIds().stream().map(String::valueOf).collect(Collectors.joining(",")));
        mbdBalanceRateMapper.insert(mbdBalanceRate);
        // 保存 mbdBalanceRateItem
        for (String woId : reqVo.getApsWorkOrderId()) {
            MbdBalanceRateItem mbdBalanceRateItem = new MbdBalanceRateItem();
            mbdBalanceRateItem.setOrderId(reqVo.getOrderId());
            mbdBalanceRateItem.setName(woId);
            mbdBalanceRateItem.setDimensionType(2);
            mbdBalanceRateItemMapper.insert(mbdBalanceRateItem);
        }
    }

    public List<MbdBalanceGroupListResVo> listBalanceRateGroup(MbdBalanceGroupListReqVo reqVo) {
        List<MbdBalanceGroupListResVo> result = new ArrayList<>();
        List<DictData> phaseWorkGroup = dictDataMapper.findByDictTypeCode("BalanceRatePhaseWorkGroup");
        for (DictData dictData : phaseWorkGroup) {
            for (String workGroupNumber : dictData.getDictValue().split(",")) {
                MbdBalanceGroupListResVo item = new MbdBalanceGroupListResVo();
                item.setWorkGroupNumber(workGroupNumber);
                item.setPhaseCode(dictData.getDictLabel());
                result.add(item);
            }
        }
        if (result.isEmpty()) {
            return result;
        }
        Map<String, Group> groupMap = groupMapper.lambdaQuery()
                .in(Group::getMyCode, result.stream().map(MbdBalanceGroupListResVo::getWorkGroupNumber).collect(Collectors.toList()))
                .list().stream().collect(Collectors.toMap(Group::getMyCode, Function.identity(), (a, b) -> b));
        for (MbdBalanceGroupListResVo item : result) {
            if (groupMap.containsKey(item.getWorkGroupNumber())) {
                item.setWorkGroupName(groupMap.get(item.getWorkGroupNumber()).getName());
            }
        }
        return result;
    }

    public MbdBalanceRateOfGroupResVo balanceRateOfGroup(MbdBalanceRateOfGroupReqVo reqVo) {
        MbdBalanceRateOfGroupResVo resVo = new MbdBalanceRateOfGroupResVo();
        MbdBalanceRate mbdBalanceRate = mbdBalanceRateMapper.selectOne(Wrappers.<MbdBalanceRate>lambdaQuery().eq(MbdBalanceRate::getOrderId, reqVo.getId()));
        Set<Integer> orderIds = new HashSet<>();
        orderIds.add(reqVo.getId());
        if (mbdBalanceRate != null && StringUtils.isNotBlank(mbdBalanceRate.getMixedOrderIds())) {
            orderIds.addAll(Arrays.stream(mbdBalanceRate.getMixedOrderIds().split(",")).map(Integer::parseInt).collect(Collectors.toList()));
        }
        resVo.setName(getBalanceRateName(mbdBalanceRate));

        List<Order> orders = orderMapper.selectMbdList(Wrappers.query().in("t.id", orderIds));
        Map<Integer, OrderBom> orderBomMap = orderBomMapper.listByQuery(Wrappers.query().in("order_id", orderIds)).stream().collect(Collectors.toMap(OrderBom::getId, Function.identity()));
        List<OrderPhase> orderPhaseList = orderPhaseMapper.listByQuery(Wrappers.query().in("order_id", orderIds));
        Map<Group, List<Station>> groupStationMap = getRelevantResource(orderPhaseList.stream().map(OrderPhase::getWorkGroupNumber).distinct().collect(Collectors.toList()));




        resVo.setOrderQty(orders.stream().filter(order -> order.getId().equals(reqVo.getId())).mapToInt(Order::getOrderQty).sum());

        applyChangeOfGroup(reqVo);

        MbdBalanceRateOfGroupResVo.BalanceRateData pro = getBalanceRateDataOfGroup(orders, orderBomMap, orderPhaseList, groupStationMap, null, null, reqVo.getProPhase());
        resVo.setPro(pro);

        List<MbdBalanceRateItem> mbdBalanceRateItems = getConfigItemsOfGroup(reqVo.getId());
        Map<String, MbdBalanceRateItem> rateItemMap = mbdBalanceRateItems.stream().collect(Collectors.toMap(MbdBalanceRateItem::getName, Function.identity()));
        MbdBalanceRateOfGroupResVo.BalanceRateData customize = getBalanceRateDataOfGroup(orders, orderBomMap, orderPhaseList, groupStationMap, rateItemMap, pro, reqVo.getCustomPhase());
        resVo.setCustomize(customize);

        return resVo;
    }

    private List<MbdBalanceRateItem> getConfigItemsOfGroup(Integer orderId) {
        List<MbdBalanceRateItem> items = getItems(orderId, 2);
        if (!items.isEmpty()) {
            return items;
        }
        BaseIdReqVo reqVo = new BaseIdReqVo();
        reqVo.setId(orderId);
        getBalanceConfigOfGroup(reqVo);
        return getItems(orderId, 2);
    }

    private List<MbdBalanceRateItem> getItems(Integer orderId, int dimensionType) {
        return mbdBalanceRateItemMapper.selectList(Wrappers.<MbdBalanceRateItem>lambdaQuery().eq(MbdBalanceRateItem::getOrderId, orderId).eq(MbdBalanceRateItem::getDimensionType, dimensionType));
    }

    private MbdBalanceRateOfGroupResVo.BalanceRateData getBalanceRateDataOfGroup(List<Order> orders, Map<Integer, OrderBom> orderBomMap, List<OrderPhase> orderPhaseList, Map<Group, List<Station>> groupStationMap, Map<String, MbdBalanceRateItem> checkMap, MbdBalanceRateOfGroupResVo.BalanceRateData pro, List<MbdBalanceRateOfGroupResVo.PhaseData> reqPhaseDataList) {

        MbdBalanceRateOfGroupResVo.BalanceRateData resVo = new MbdBalanceRateOfGroupResVo.BalanceRateData();
        List<DictData> phaseWorkGroup = dictDataMapper.findByDictTypeCode("BalanceRatePhaseWorkGroup");
        Map<String, Group> groupMap = groupStationMap.keySet().stream().collect(Collectors.toMap(Group::getMyCode, Function.identity(), (a, b) -> b));
        for (DictData dictData : phaseWorkGroup) {
            String phaseCodes = dictData.getDictLabel();
            String[] workGroupNumbers = dictData.getDictValue().split(",");
            MbdBalanceRateOfGroupResVo.PhaseData proPhaseData = pro == null ? null : pro.getPhase().stream().filter(e -> e.getPhaseCode().equals(phaseCodes)).findFirst().orElse(null);
            if (reqPhaseDataList != null) {
                MbdBalanceRateOfGroupResVo.PhaseData reqPhaseData = reqPhaseDataList.stream().filter(e -> e.getPhaseCode().equals(phaseCodes)).findFirst().orElse(null);
                if (reqPhaseData != null) {
                    MbdBalanceRateOfGroupResVo.PhaseData phaseData = new MbdBalanceRateOfGroupResVo.PhaseData();
                    phaseData.setPhaseCode(reqPhaseData.getPhaseCode());
                    phaseData.setPhaseName(reqPhaseData.getPhaseName());
                    resVo.getPhase().add(phaseData);
                    for (MbdBalanceRateOfGroupResVo.GroupData reqGroupData : reqPhaseData.getGroupDataList()) {
                        MbdBalanceRateOfGroupResVo.GroupData groupData = new MbdBalanceRateOfGroupResVo.GroupData();
                        phaseData.getGroupDataList().add(groupData);
                        if (!reqGroupData.isHidden()) {
                            groupData.setWorkGroupNumber(reqGroupData.getWorkGroupNumber());
                            Group group = groupMap.get(reqGroupData.getWorkGroupNumber());
                            if (group == null) {
                                throw SyExceptionUtils.e(String.format("工作组%s不存在", reqGroupData.getWorkGroupNumber()));
                            }
                            groupData.setWorkGroupName(group.getName());
                            groupData.setWorkTimeMinutes(reqGroupData.getWorkTimeMinutes());
                            groupData.setWorkAbility(reqGroupData.getWorkAbility());
                            groupData.setStationCount(reqGroupData.getStationCount());
                            if (reqGroupData.getStationCountSetup() != null && reqGroupData.getStationCountSetup().compareTo(BigDecimal.ZERO) > 0) {
                                groupData.setStationCount(reqGroupData.getStationCountSetup());
                            }
                            groupData.setStationCountSetup(reqGroupData.getStationCountSetup());
                            if (proPhaseData != null) { // 重写自定义的工位数
                                for (MbdBalanceRateOfGroupResVo.GroupData proR : proPhaseData.getGroupDataList()) {
                                    if (Objects.equals(groupData.getWorkGroupNumber(), proR.getWorkGroupNumber())) {
                                        groupData.setStationCount(proR.getStationCount());
                                        break;
                                    }
                                }
                            }
                            groupData.setStationTotal(groupData.getWorkAbility().multiply(groupData.getStationCount()));
                            groupData.setProcessRhythm(groupData.getWorkTimeMinutes().divide(groupData.getStationTotal(), 2, RoundingMode.HALF_UP));
                            groupData.setHidden(false);
                            groupData.setMaterials(reqGroupData.getMaterials());
                        } else {
                            groupData.setHidden(true);
                        }
                    }
                    continue;
                }
            }
            List<MbdBalanceRateOfGroupResVo.GroupData> groupDataList = new ArrayList<>();
            for (String workGroupNumber : workGroupNumbers) {
                List<OrderPhase> groupOrderPhaseList = orderPhaseList.stream()
                        .filter(phase -> workGroupNumber.equals(phase.getWorkGroupNumber()))
                        .collect(Collectors.toList());
                if (groupOrderPhaseList.isEmpty()) {
                    continue;
                }
                MbdBalanceRateOfGroupResVo.GroupData groupData = new MbdBalanceRateOfGroupResVo.GroupData();
                if ((checkMap == null || checkMap.containsKey(workGroupNumber))) {
                    groupData.setWorkGroupNumber(workGroupNumber);
                    Group group = groupMap.get(workGroupNumber);
                    if (group == null) {
                        throw SyExceptionUtils.e(String.format("工作组%s不存在", workGroupNumber));
                    }
                    groupData.setWorkGroupName(group.getName());
                    groupData.setWorkTimeMinutes(calculateWorkTimeMinutes(orders, orderBomMap, groupOrderPhaseList));
                    List<Station> groupStations = groupStationMap.get(group);
                    if (groupStations == null) {
                        throw SyExceptionUtils.e(String.format("工作组%s无工位", workGroupNumber));
                    }
                    groupData.setWorkAbility(groupStations.get(0).getWorkAbility());
                    groupData.setStationCount(BigDecimal.valueOf(groupStations.size()));
                    if (proPhaseData != null) { // 重写自定义的工位数
                        for (MbdBalanceRateOfGroupResVo.GroupData proR : proPhaseData.getGroupDataList()) {
                            if (Objects.equals(groupData.getWorkGroupNumber(), proR.getWorkGroupNumber())) {
                                groupData.setStationCount(proR.getStationCount());
                                break;
                            }
                        }
                    }

                    groupData.setStationTotal(groupData.getWorkAbility().multiply(groupData.getStationCount()));
                    groupData.setProcessRhythm(groupData.getWorkTimeMinutes().divide(groupData.getStationTotal(), 2, RoundingMode.HALF_UP));
                    groupData.setHidden(false);
                    groupData.setMaterials(listGroupDataDetail(orders, orderBomMap, groupOrderPhaseList));
                } else {
                    groupData.setHidden(true);
                }
                groupDataList.add(groupData);
            }
            if (groupDataList.isEmpty()) {
                continue;
            }
            List<String> phaseCodeList = Arrays.asList(phaseCodes.split("/"));
            Map<String, Phase> phaseMap = phaseMapper.lambdaQuery()
                    .in(Phase::getMyCode, phaseCodeList)
                    .list().stream()
                    .collect(Collectors.toMap(Phase::getMyCode, Function.identity(), (v1, v2) -> v1));
            MbdBalanceRateOfGroupResVo.PhaseData phaseData = new MbdBalanceRateOfGroupResVo.PhaseData();
            phaseData.setPhaseCode(phaseCodes);
            phaseData.setPhaseName(phaseCodeList.stream().map(e -> phaseMap.containsKey(e) ? phaseMap.get(e).getName() : "切割").collect(Collectors.joining("/")));
            phaseData.setGroupDataList(groupDataList);
            resVo.getPhase().add(phaseData);
        }

        // 总工时
        resVo.setTotalWorkingHours(resVo.getPhase().stream().flatMap(e -> e.getGroupDataList().stream().filter(e1 -> !e1.isHidden())).map(MbdBalanceRateOfGroupResVo.GroupData::getWorkTimeMinutes).reduce(BigDecimal.ZERO, BigDecimal::add));

        // 总工位数
        BigDecimal totalStationCount = resVo.getPhase().stream().flatMap(e -> e.getGroupDataList().stream().filter(e1 -> !e1.isHidden())).map(MbdBalanceRateOfGroupResVo.GroupData::getStationTotal).reduce(BigDecimal.ZERO, BigDecimal::add);
        resVo.setTotalStationCount(totalStationCount);

        // 瓶颈工序节拍
        String maxProcessStage = null;
        MbdBalanceRateOfGroupResVo.GroupData maxProcessRhythm = null;
        for (MbdBalanceRateOfGroupResVo.PhaseData phase : resVo.getPhase()) {
            for (MbdBalanceRateOfGroupResVo.GroupData vo : phase.getGroupDataList()) {
                if (vo.isHidden()) {
                    continue;
                }
                if (maxProcessRhythm == null || maxProcessRhythm.getProcessRhythm().compareTo(vo.getProcessRhythm()) < 0) {
                    maxProcessRhythm = vo;
                    maxProcessStage = phase.getPhaseCode();
                }
            }
        }
        if (maxProcessRhythm == null) {
            throw SyExceptionUtils.e("没有瓶颈工序");
        }
        resVo.setBottleneckProcess(maxProcessRhythm.getProcessRhythm());
        resVo.setBottleneckProcessStage(maxProcessStage);
        resVo.setBottleneckProcessName(maxProcessRhythm.getWorkGroupName());

        // 平衡率=总工时/（工位数量*瓶颈工序节拍）
        resVo.setBalanceRate(resVo.getTotalWorkingHours().divide(totalStationCount.multiply(maxProcessRhythm.getProcessRhythm()), 4, RoundingMode.HALF_UP));
        resVo.setEquilibriumLossRate(new BigDecimal(1).subtract(resVo.getBalanceRate()));

        // 调整策略
        /*calculateStrategy(order, resVo.getPhase(), maxProcessRhythm);*/

        return resVo;
    }

    private List<MbdBalanceRateOfGroupResVo.MaterialData> listGroupDataDetail(List<Order> orders, Map<Integer, OrderBom> orderBomMap, List<OrderPhase> phases) {
        Map<Integer, Order> orderMap = orders.stream().collect(Collectors.toMap(Order::getId, Function.identity()));
        return phases.stream().map(phase -> {
            Order order = orderMap.get(phase.getOrderId());
            OrderBom orderBom = orderBomMap.get(phase.getBomId());
            MbdBalanceRateOfGroupResVo.MaterialData materialData = new MbdBalanceRateOfGroupResVo.MaterialData();
            materialData.setOrderId(phase.getOrderId());
            materialData.setBomId(phase.getBomId());
            materialData.setPhaseId(phase.getId());
            materialData.setParentPlmid(orderBom.getParentBomCode());
            materialData.setPlmid(orderBom.getBomCode());
            materialData.setMaterName(orderBom.getMaterName());
            materialData.setTotalCount(orderBom.getTotalCount().multiply(BigDecimal.valueOf(order.getOrderQty())));
            materialData.setWorkTime(BigDecimal.valueOf(phase.getWorkTime()).divide(new BigDecimal(60), 2, RoundingMode.HALF_UP));
            materialData.setTotalWorkTime(materialData.getTotalCount().multiply(materialData.getWorkTime()).setScale(2, RoundingMode.HALF_UP));
            materialData.setTexture(orderBom.getTexture());
            materialData.setMaterHigh(orderBom.getMaterHigh());
            materialData.setMaterLength(null);
            materialData.setMaterWidth(null);
            materialData.setMaterWeight(orderBom.getMaterWeight());
            materialData.setWorkChain(orderBom.getWorkChain());
            return materialData;
        }).collect(Collectors.toList());
    }

    private BigDecimal calculateWorkTimeMinutes(List<Order> orders, Map<Integer, OrderBom> orderBomMap, List<OrderPhase> phases) {
        return phases.stream()
                .map(phase -> {
                    int orderQty = orders.stream().filter(e -> e.getId().equals(phase.getOrderId())).mapToInt(Order::getOrderQty).findAny().orElse(0);
                    OrderBom orderBom = orderBomMap.get(phase.getBomId());
                    BigDecimal totalSecond = new BigDecimal(phase.getWorkTime()).multiply(orderBom.getTotalCount()).multiply(new BigDecimal(orderQty));
                    return totalSecond.divide(new BigDecimal(60), 2, RoundingMode.HALF_UP);
                })
                .reduce(BigDecimal.ZERO, BigDecimal::add);
    }

    private void applyChangeOfGroup(MbdBalanceRateOfGroupReqVo reqVo) {
        List<MbdBalanceRateOfGroupResVo.PhaseData> phase = reqVo.getProPhase();
        MbdBalanceRateDataPhaseTimeChange change = reqVo.getWorkTimeChange();
        if (phase == null || change == null) {
            return;
        }
        List<MbdBalanceRateOfGroupResVo.GroupData> changedStage = phase.stream().filter(e -> e.getPhaseCode().equals(change.getChangedStage())).findAny().map(MbdBalanceRateOfGroupResVo.PhaseData::getGroupDataList).orElse(null);
        List<MbdBalanceRateOfGroupResVo.GroupData> influencedStage = phase.stream().filter(e -> e.getPhaseCode().equals(change.getInfluencedStage())).findAny().map(MbdBalanceRateOfGroupResVo.PhaseData::getGroupDataList).orElse(null);
        if (changedStage == null || influencedStage == null) {
            return;
        }
        MbdBalanceRateOfGroupResVo.GroupData changedPhase = changedStage.stream().filter(e -> !e.isHidden() && e.getWorkGroupNumber().equals(change.getChangedPhaseCode())).findFirst().orElse(null);
        MbdBalanceRateOfGroupResVo.GroupData influencedPhase = influencedStage.stream().filter(e -> !e.isHidden() && e.getWorkGroupNumber().equals(change.getInfluencedPhaseCode())).findFirst().orElse(null);
        if (changedPhase == null || influencedPhase == null) {
            return;
        }
        MbdBalanceRateOfGroupResVo.MaterialData changedMaterial = changedPhase.getMaterials().stream().filter(e -> e.getPhaseId().equals(change.getId())).findFirst().orElse(null);
        MbdBalanceRateOfGroupResVo.MaterialData influencedMaterial = calculateInfluencedMaterial(influencedPhase, changedMaterial);
        if (changedMaterial == null || influencedMaterial == null) {
            return;
        }
        // 修改数量
        changedMaterial.setTotalCount(changedMaterial.getTotalCount().add(change.getChangedValue()));
        influencedMaterial.setTotalCount(influencedMaterial.getTotalCount().subtract(change.getChangedValue()));
        // 修改工时
        BigDecimal changedTimeValue = change.getChangedValue().multiply(changedMaterial.getWorkTime());
        changedMaterial.setTotalWorkTime(changedMaterial.getTotalWorkTime().add(changedTimeValue));
        influencedMaterial.setTotalWorkTime(influencedMaterial.getTotalWorkTime().subtract(changedTimeValue));
        changedPhase.setWorkTimeMinutes(changedPhase.getWorkTimeMinutes().add(changedTimeValue));
        influencedPhase.setWorkTimeMinutes(influencedPhase.getWorkTimeMinutes().subtract(changedTimeValue));
        // 移除为0的项
        changedPhase.getMaterials().removeIf(e -> e.getTotalCount().compareTo(BigDecimal.ZERO) == 0);
        influencedPhase.getMaterials().removeIf(e -> e.getTotalCount().compareTo(BigDecimal.ZERO) == 0);
 /*       if (changedPhase.getMaterials().isEmpty()) {
            changedStage.remove(changedPhase);
        }
        if (influencedPhase.getMaterials().isEmpty()) {
            influencedStage.remove(influencedPhase);
        }*/
        this.saveOpOfGroup(reqVo.getId(), reqVo.getWorkTimeChange(), changedPhase, influencedPhase);
    }

    private MbdBalanceRateOfGroupResVo.MaterialData calculateInfluencedMaterial(MbdBalanceRateOfGroupResVo.GroupData groupData, MbdBalanceRateOfGroupResVo.MaterialData changedMaterial) {
        if (changedMaterial == null) {
            return null;
        }
        MbdBalanceRateOfGroupResVo.MaterialData influencedMaterial = groupData.getMaterials().stream().filter(materialData -> materialData.getPhaseId().equals(changedMaterial.getPhaseId())).findFirst().orElse(null);
        if (influencedMaterial != null) {
            return influencedMaterial;
        }
        influencedMaterial = new MbdBalanceRateOfGroupResVo.MaterialData();
        BeanUtils.copyProperties(changedMaterial, influencedMaterial);
        influencedMaterial.setTotalCount(BigDecimal.ZERO);
        influencedMaterial.setTotalWorkTime(BigDecimal.ZERO);
        groupData.getMaterials().add(influencedMaterial);
        return influencedMaterial;
    }

    private void saveOpOfGroup(Integer orderId, MbdBalanceRateDataPhaseTimeChange workTimeChange, MbdBalanceRateOfGroupResVo.GroupData changedPhase, MbdBalanceRateOfGroupResVo.GroupData influencedPhase) {
        MbdBalanceRateOp op = new MbdBalanceRateOp();
        op.setOrderId(orderId);
        op.setChangedStage(workTimeChange.getChangedStage());
        op.setChangedPhaseCode(workTimeChange.getChangedPhaseCode());
        op.setChangedPhaseName(changedPhase.getWorkGroupName());
        op.setChangedValue(workTimeChange.getChangedValue());
        op.setInfluencedPhaseCode(workTimeChange.getInfluencedPhaseCode());
        op.setInfluencedStage(workTimeChange.getInfluencedStage());
        op.setInfluencedPhaseName(influencedPhase.getWorkGroupName());
        op.setDimensionType(2);
        mbdBalanceRateOpMapper.insert(op);
    }

    private Map<Group, List<Station>> getRelevantResource(List<String> groupNumbers) {
        List<Group> groups = groupMapper.lambdaQuery()
                .in(Group::getMyCode, groupNumbers)
                .list();
        if (groups.isEmpty()) {
            return new HashMap<>();
        }

        List<Integer> groupIds = groups.stream()
                .map(Group::getId)
                .collect(Collectors.toList());
        List<Station> stations = stationMapper.lambdaQuery()
                .in(Station::getGroupId, groupIds)
                .list();
        List<Shift> shiftList = shiftMapper.lambdaQuery().list();
        for (Station station : stations) {
            List<Shift> shifts = shiftList.stream().filter(shift -> shift.getShiftTypeId().equals(station.getShiftTypeId())).collect(Collectors.toList());
            // shifts （总有效时长 / 总工作时间 ）
            BigDecimal validTimeResult = shifts.stream().map(e -> new BigDecimal(e.getValidTime())).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal workTimeResult = shifts.stream().map(e -> new BigDecimal(e.getTotalWorkTime())).reduce(BigDecimal.ZERO, BigDecimal::add);
            station.setWorkAbility(validTimeResult.divide(workTimeResult, 2, RoundingMode.HALF_UP));
        }
        return groups.stream()
                .collect(Collectors.toMap(Function.identity(),
                        group -> stations.stream()
                                .filter(sg -> sg.getGroupId().equals(group.getId()))
//                                .flatMap(sg -> stations.stream().filter(station -> station.getId().equals(sg.getStationId())))
                                .collect(Collectors.toList()),
                        (a, b) -> b));
    }

    public List<MbdBalanceRateDataListResVo> listBalanceRateDataOfGroup(MbdBalanceRateDataListReqVo reqVo) {
        reqVo.setDimensionType(2);
        return listBalanceRateData(reqVo);
    }

    public void saveBalanceRateDataOfGroup(MbdBalanceRateDataOfGroupSaveReqVo reqVo) {
        MbdBalanceRateData exist = null;
        if (reqVo.getId() != null) {
            exist = mbdBalanceRateDataMapper.selectById(reqVo.getId());
            if (exist == null) {
                throw SyExceptionUtils.e("留痕记录不存在" + reqVo.getId());
            }
        }
        MbdBalanceRateData entity = exist == null ? new MbdBalanceRateData() : exist;
        entity.setName(reqVo.getName());
        entity.setOrderId(reqVo.getOrderId());
        entity.setOrderQty(reqVo.getOrderQty());
        entity.setDataPro(JsonUtils.toJsonString(reqVo.getPro()));
        entity.setDataCustomize(JsonUtils.toJsonString(reqVo.getCustomize()));
        entity.setDimensionType(2);
        mbdBalanceRateDataMapper.insertOrUpdate(entity);
        // 关联操作记录
        mbdBalanceRateOpMapper.lambdaUpdate()
                .eq(MbdBalanceRateOp::getOrderId, reqVo.getOrderId())
                .eq(MbdBalanceRateOp::getDimensionType, 2)
                .isNull(MbdBalanceRateOp::getDataId)
                .set(MbdBalanceRateOp::getDataId, entity.getId())
                .update();
    }

    public List<MbdBalanceRateDataComparisonOfGroupResVo> balanceRateComparisonOfGroup(MbdBalanceRateDataComparisonOfGroupReqVo reqVo) {
        return mbdBalanceRateDataMapper.lambdaQuery()
                .in(MbdBalanceRateData::getId, reqVo.getIds())
                .orderByAsc(MbdBalanceRateData::getCreateDateTime)
                .list().stream()
                .map(balanceRateData -> {
                    try {
                        balanceRateData.setProOfGroup(objectMapper.readValue(balanceRateData.getDataPro(), MbdBalanceRateOfGroupResVo.BalanceRateData.class));
                    } catch (JsonProcessingException e) {
                        throw SyExceptionUtils.e("数据异常");
                    }
                    MbdBalanceRateDataComparisonOfGroupResVo resVo = new MbdBalanceRateDataComparisonOfGroupResVo();
                    BeanUtils.copyProperties(balanceRateData.getProOfGroup(), resVo);
                    resVo.setId(balanceRateData.getId());
                    resVo.setSaveTime(balanceRateData.getCreateDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss")));
                    resVo.setOrderId(balanceRateData.getOrderId());
                    return resVo;
                })
                .collect(Collectors.toList());
    }

    public Workbook exportBalanceRateComparisonOfGroup(MbdBalanceRateDataComparisonOfGroupReqVo reqVo) {
        return null;
    }

    public BaseSelectResVo select() {
        BaseListResDto<BomInfoSelectResDto> select = momSrvMbdApiClient.select();

        List<BaseSelectItemResVo> productCode = select.getData()
                .stream()
                .map(e -> {
                    BaseSelectItemResVo r = new BaseSelectItemResVo();
                    r.setText(e.getPlmId());
                    r.setValue(e.getPlmId());
                    r.getAttr().put("productCode", e.getProductCode());
                    return r;
                })
                .collect(Collectors.toList());

        return BaseSelectResVo.of(productCode);
    }
    public MbdBalanceRateGetDataResVo getBalanceRateData(BaseIdReqVo reqVo) {
        MbdBalanceRateData balanceRateData = mbdBalanceRateDataMapper.selectById(reqVo.getId());
        if (balanceRateData == null) {
            throw SyExceptionUtils.e("留痕数据不存在");
        }
        MbdBalanceRateDataResVo pro;
        MbdBalanceRateDataResVo customize;
        try {
            pro = objectMapper.readValue(balanceRateData.getDataPro(), MbdBalanceRateDataResVo.class);
            customize = objectMapper.readValue(balanceRateData.getDataCustomize(), MbdBalanceRateDataResVo.class);
        } catch (JsonProcessingException e) {
            throw SyExceptionUtils.e("数据异常");
        }
        MbdBalanceRateGetDataResVo resVo = new MbdBalanceRateGetDataResVo();
        resVo.setId(balanceRateData.getId());
        resVo.setName(balanceRateData.getName());
        resVo.setOrderQty(balanceRateData.getOrderQty());
        resVo.setPro(pro);
        resVo.setCustomize(customize);
        return resVo;
    }

    public MbdBalanceRateGetDataOfGroupResVo getBalanceRateDataOfGroup(BaseIdReqVo reqVo) {
        MbdBalanceRateData balanceRateData = mbdBalanceRateDataMapper.selectById(reqVo.getId());
        if (balanceRateData == null) {
            throw SyExceptionUtils.e("留痕数据不存在");
        }
        MbdBalanceRateGetDataOfGroupResVo.BalanceRateData pro;
        MbdBalanceRateGetDataOfGroupResVo.BalanceRateData customize;
        try {
            pro = objectMapper.readValue(balanceRateData.getDataPro(), MbdBalanceRateGetDataOfGroupResVo.BalanceRateData.class);
            customize = objectMapper.readValue(balanceRateData.getDataCustomize(), MbdBalanceRateGetDataOfGroupResVo.BalanceRateData.class);
        } catch (JsonProcessingException e) {
            throw SyExceptionUtils.e("数据异常");
        }
        MbdBalanceRateGetDataOfGroupResVo resVo = new MbdBalanceRateGetDataOfGroupResVo();
        resVo.setId(balanceRateData.getId());
        resVo.setName(balanceRateData.getName());
        resVo.setOrderQty(balanceRateData.getOrderQty());
        resVo.setPro(pro);
        resVo.setCustomize(customize);
        return resVo;
    }

    public MbdBalanceRateOfFactoryResVo balanceRateOfFactory(MbdBalanceRateOfFactoryReqVo reqVo) {

        MbdBalanceRateOfFactoryResVo resVo;
        // 查询两区平衡率原始数据
        if (reqVo.getIds() == null || reqVo.getIds().isEmpty()) {
            resVo = balanceRateOfFactoryFromOrder(reqVo.getId());
        } else {
            resVo = balanceRateOfFactoryFromRecord(reqVo.getIds());
        }
        Order order = orderMapper.selectOneAll(reqVo.getId());
        resVo.setOrderQty(order.getOrderQty());
        return resVo;
    }

    private MbdBalanceRateOfFactoryResVo balanceRateOfFactoryFromRecord(List<Integer> ids) {
        MbdBalanceRateOfFactoryResVo.GraphData top = new MbdBalanceRateOfFactoryResVo.GraphData();
        MbdBalanceRateOfFactoryResVo.GraphData all = new MbdBalanceRateOfFactoryResVo.GraphData();
        MbdBalanceRateOfFactoryResVo.GraphData critical = new MbdBalanceRateOfFactoryResVo.GraphData();
        MbdBalanceRateOfFactoryResVo.StationAllocationData allocation = new MbdBalanceRateOfFactoryResVo.StationAllocationData();
        BigDecimal allMaxProcessRhythm = BigDecimal.ZERO;
        BigDecimal criticalMaxProcessRhythm = BigDecimal.ZERO;
        for (Integer id : ids) {
            MbdBalanceRateData balanceRateData = mbdBalanceRateDataMapper.selectById(id);

            Order order = orderMapper.selectOneAll(balanceRateData.getOrderId());

            List<OrderBom> orderBomList = orderBomMapper.listByQuery(Wrappers.query().eq("order_id", order.getId()));
            Map<String, OrderBom> orderBomMap = orderBomList.stream().collect(Collectors.toMap(OrderBom::getMaterCode, Function.identity(), (e1, e2) -> e1));

            List<OrderPhase> orderPhaseList = orderPhaseMapper.listByQuery(Wrappers.query().eq("order_id", order.getId()));
            Map<Integer, List<OrderPhase>> orderPhaseMap = orderPhaseList.stream().collect(Collectors.groupingBy(OrderPhase::getBomId));

            if (balanceRateData.getDimensionType() == null || balanceRateData.getDimensionType() == 1) {
                MbdBalanceRateDataResVo pro;
                MbdBalanceRateDataResVo customize;
                try {
                    pro = objectMapper.readValue(balanceRateData.getDataPro(), MbdBalanceRateDataResVo.class);
                    customize = objectMapper.readValue(balanceRateData.getDataCustomize(), MbdBalanceRateDataResVo.class);
                } catch (JsonProcessingException e) {
                    throw SyExceptionUtils.e("数据异常");
                }
                // 提取所有工序
                all.getGraphData().addAll(extractPhase(order, pro.getPhase()));
                critical.getGraphData().addAll(extractPhase(order, customize.getPhase()));
                // 计算平衡率
                if (copyBalanceRateOverview(all, pro, allMaxProcessRhythm)) {
                    allMaxProcessRhythm = pro.getBottleneckProcess();
                }
                if (copyBalanceRateOverview(critical, customize, criticalMaxProcessRhythm)) {
                    criticalMaxProcessRhythm = customize.getBottleneckProcess();
                }
                // 计算工位分配表
                calculateAllocation(allocation, order, orderBomMap, orderPhaseMap, pro);
            } else {
                MbdBalanceRateOfGroupResVo.BalanceRateData pro;
                MbdBalanceRateOfGroupResVo.BalanceRateData customize;
                try {
                    pro = objectMapper.readValue(balanceRateData.getDataPro(), MbdBalanceRateOfGroupResVo.BalanceRateData.class);
                    customize = objectMapper.readValue(balanceRateData.getDataCustomize(), MbdBalanceRateOfGroupResVo.BalanceRateData.class);
                } catch (JsonProcessingException e) {
                    throw SyExceptionUtils.e("数据异常");
                }
                // 提取所有工序
                all.getGraphData().addAll(extractPhase(order, pro.getPhase()));
                critical.getGraphData().addAll(extractPhase(order, customize.getPhase()));
                // 计算平衡率
                if (copyBalanceRateOverview(all, pro, allMaxProcessRhythm)) {
                    allMaxProcessRhythm = pro.getBottleneckProcess();
                }
                if (copyBalanceRateOverview(critical, customize, criticalMaxProcessRhythm)) {
                    criticalMaxProcessRhythm = customize.getBottleneckProcess();
                }
            }
        }
        top.setGraphData(new ArrayList<>(all.getGraphData()));
        top.getGraphData().sort(Comparator.comparing(MbdBalanceRateOfFactoryResVo.GraphItem::getValue).reversed());
        top.setGraphData(top.getGraphData().subList(0, Math.min(10, top.getGraphData().size())));
        if (!top.getGraphData().isEmpty()) {
            top.setBottleneckProcess(top.getGraphData().get(0).getValue());
            top.setBottleneckProcessStage(top.getGraphData().get(0).getPhaseCode());
            top.setBottleneckProcessName(top.getGraphData().get(0).getPhaseName());
        }
        MbdBalanceRateOfFactoryResVo resVo = new MbdBalanceRateOfFactoryResVo();
        resVo.setTop(top);
        resVo.setAll(all);
        resVo.setCritical(critical);
        resVo.setAllocation(allocation);
        return resVo;
    }

    private MbdBalanceRateOfFactoryResVo balanceRateOfFactoryFromOrder(Integer originalOrderId) {
        List<Integer> orderIds = listRelevantOrderIds(originalOrderId);
        MbdBalanceRateOfFactoryResVo.GraphData top = new MbdBalanceRateOfFactoryResVo.GraphData();
        MbdBalanceRateOfFactoryResVo.GraphData all = new MbdBalanceRateOfFactoryResVo.GraphData();
        MbdBalanceRateOfFactoryResVo.GraphData critical = new MbdBalanceRateOfFactoryResVo.GraphData();
        MbdBalanceRateOfFactoryResVo.StationAllocationData allocation = new MbdBalanceRateOfFactoryResVo.StationAllocationData();
        BigDecimal allMaxProcessRhythm = BigDecimal.ZERO;
        BigDecimal criticalMaxProcessRhythm = BigDecimal.ZERO;
        for (Integer orderId : orderIds) {
            MbdBalanceRateReqVo balanceRateReqVo = new MbdBalanceRateReqVo();
            balanceRateReqVo.setId(orderId);
            MbdBalanceRateResVo balanceRate = balanceRate(balanceRateReqVo);

            MbdBalanceRateOfGroupReqVo balanceRateOfGroupReqVo = new MbdBalanceRateOfGroupReqVo();
            balanceRateOfGroupReqVo.setId(orderId);
            MbdBalanceRateOfGroupResVo balanceRateOfGroup = balanceRateOfGroup(balanceRateOfGroupReqVo);

            Order order = orderMapper.selectOneAll(orderId);

            List<OrderBom> orderBomList = orderBomMapper.listByQuery(Wrappers.query().eq("order_id", order.getId()));
            Map<String, OrderBom> orderBomMap = orderBomList.stream().collect(Collectors.toMap(OrderBom::getMaterCode, Function.identity(), (e1, e2) -> e1));

            List<OrderPhase> orderPhaseList = orderPhaseMapper.listByQuery(Wrappers.query().eq("order_id", order.getId()));
            Map<Integer, List<OrderPhase>> orderPhaseMap = orderPhaseList.stream().collect(Collectors.groupingBy(OrderPhase::getBomId));

             // 提取所有工序
            all.getGraphData().addAll(extractPhase(order, balanceRateOfGroup.getPro().getPhase()));
            all.getGraphData().addAll(extractPhase(order, balanceRate.getPro().getPhase()));
            critical.getGraphData().addAll(extractPhase(order, balanceRateOfGroup.getCustomize().getPhase()));
            critical.getGraphData().addAll(extractPhase(order, balanceRate.getCustomize().getPhase()));
            // 计算平衡率
            if (copyBalanceRateOverview(all, balanceRate.getPro(), allMaxProcessRhythm)) {
                allMaxProcessRhythm = balanceRate.getPro().getBottleneckProcess();
            }
            if (copyBalanceRateOverview(all, balanceRateOfGroup.getPro(), allMaxProcessRhythm)) {
                allMaxProcessRhythm = balanceRateOfGroup.getPro().getBottleneckProcess();
            }
            if (copyBalanceRateOverview(critical, balanceRate.getCustomize(), criticalMaxProcessRhythm)) {
                criticalMaxProcessRhythm = balanceRate.getCustomize().getBottleneckProcess();
            }
            if (copyBalanceRateOverview(critical, balanceRateOfGroup.getCustomize(), criticalMaxProcessRhythm)) {
                criticalMaxProcessRhythm = balanceRate.getCustomize().getBottleneckProcess();
            }
            // 计算工位分配表
            calculateAllocation(allocation, order, orderBomMap, orderPhaseMap, balanceRate.getPro());
        }
        top.setGraphData(new ArrayList<>(all.getGraphData()));
        top.getGraphData().sort(Comparator.comparing(MbdBalanceRateOfFactoryResVo.GraphItem::getValue).reversed());
        top.setGraphData(top.getGraphData().subList(0, Math.min(10, top.getGraphData().size())));
        if (!top.getGraphData().isEmpty()) {
            top.setBottleneckProcess(top.getGraphData().get(0).getValue());
            top.setBottleneckProcessStage(top.getGraphData().get(0).getPhaseCode());
            top.setBottleneckProcessName(top.getGraphData().get(0).getPhaseName());
        }
        MbdBalanceRateOfFactoryResVo resVo = new MbdBalanceRateOfFactoryResVo();
        resVo.setTop(top);
        resVo.setAll(all);
        resVo.setCritical(critical);
        resVo.setAllocation(allocation);
        return resVo;
    }

    private void calculateAllocation(MbdBalanceRateOfFactoryResVo.StationAllocationData allocation,
                                     Order order, Map<String, OrderBom> bomMap, Map<Integer, List<OrderPhase>> phaseMap,
                                     MbdBalanceRateDataResVo balanceRate) {
        allocation.accumulate(order, bomMap, phaseMap, balanceRate);
    }

    private boolean copyBalanceRateOverview(MbdBalanceRateOfFactoryResVo.GraphData graphData, MbdBalanceRateOfGroupResVo.BalanceRateData balanceRateData, BigDecimal maxProcessRhythm) {
        if (balanceRateData.getBottleneckProcess().compareTo(maxProcessRhythm) > 0) {
            graphData.setBalanceRate(balanceRateData.getBalanceRate());
            graphData.setEquilibriumLossRate(balanceRateData.getEquilibriumLossRate());
            graphData.setBottleneckProcess(balanceRateData.getBottleneckProcess());
            graphData.setBottleneckProcessStage(balanceRateData.getBottleneckProcessStage());
            graphData.setBottleneckProcessName(balanceRateData.getBottleneckProcessName());
            graphData.setTotalWorkingHours(balanceRateData.getTotalWorkingHours());
            graphData.setTotalStationCount(balanceRateData.getTotalStationCount());
            return true;
        }
        return false;
    }

    private boolean copyBalanceRateOverview(MbdBalanceRateOfFactoryResVo.GraphData graphData, MbdBalanceRateDataResVo balanceRateData, BigDecimal maxProcessRhythm) {
        if (balanceRateData.getBottleneckProcess().compareTo(maxProcessRhythm) > 0) {
            graphData.setBalanceRate(balanceRateData.getBalanceRate());
            graphData.setEquilibriumLossRate(balanceRateData.getEquilibriumLossRate());
            graphData.setBottleneckProcess(balanceRateData.getBottleneckProcess());
            graphData.setBottleneckProcessStage(balanceRateData.getBottleneckProcessStage());
            graphData.setBottleneckProcessName(balanceRateData.getBottleneckProcessName());
            graphData.setTotalWorkingHours(balanceRateData.getTotalWorkingHours());
            graphData.setTotalStationCount(balanceRateData.getTotalStationCount());
            return true;
        }
        return false;
    }

    private List<MbdBalanceRateOfFactoryResVo.GraphItem> extractPhase(Order order, List<MbdBalanceRateOfGroupResVo.PhaseData> phase) {
        List<MbdBalanceRateOfFactoryResVo.GraphItem> result = new ArrayList<>();
        phase.forEach(stage -> stage.getGroupDataList().forEach(groupData -> {
            if (groupData.isHidden()) {
                return;
            }
            String name = String.format("%s(%s)", groupData.getWorkGroupNumber(), order.getPlmId());
            MbdBalanceRateOfFactoryResVo.GraphItem graphItem = new MbdBalanceRateOfFactoryResVo.GraphItem();
            graphItem.setName(name);
            graphItem.setValue(groupData.getProcessRhythm());
            graphItem.setPhaseCode(stage.getPhaseCode());
            graphItem.setPhaseName(groupData.getWorkGroupName());
            result.add(graphItem);
        }));
        result.sort(Comparator.comparing(MbdBalanceRateOfFactoryResVo.GraphItem::getName));
        return result;
    }

    private static List<MbdBalanceRateOfFactoryResVo.GraphItem> extractPhase(Order order, Map<String, List<MbdBalanceRateDataPhaseResVo>> phaseMap) {
        List<MbdBalanceRateOfFactoryResVo.GraphItem> result = new ArrayList<>();
        phaseMap.forEach((stage, phases) -> phases.forEach(phase -> {
            if (phase.isHidden()) {
                return;
            }
            String name = String.format("%s_%s/%s(%s)", stage, phase.getPhaseName(), phase.getPhaseCode(), order.getPlmId());
            MbdBalanceRateOfFactoryResVo.GraphItem graphItem = new MbdBalanceRateOfFactoryResVo.GraphItem();
            graphItem.setName(name);
            graphItem.setValue(phase.getProcessRhythm());
            graphItem.setPhaseCode(stage);
            graphItem.setPhaseName(phase.getPhaseName());
            result.add(graphItem);
        }));
        return result;
    }

    private List<Integer> listRelevantOrderIds(Integer orderId) {
        List<Integer> orderIds = new ArrayList<>();
        MbdBalanceRate config = mbdBalanceRateMapper.lambdaQuery()
                .eq(MbdBalanceRate::getOrderId, orderId)
                .one();
        if (config != null) {
            for (String odi : config.getMixedOrderIds().split(",")) {
                int odiInt = Integer.parseInt(odi);
                if (!orderIds.contains(odiInt)) {
                    orderIds.add(odiInt);
                }
            }
        }
        if (!orderIds.contains(orderId)) {
            orderIds.add(orderId);
        }
        return orderIds;
    }

    public List<MbdBalanceRateDataListOfFactoryResVo> listBalanceRateDataOfFactory(Integer orderId) {
        List<Integer> orderIds = listRelevantOrderIds(orderId);
        Map<Integer, Order> orderMap = orderMapper.selectMbdList(Wrappers.query().in("t.id", orderIds))
                .stream()
                .collect(Collectors.toMap(Order::getId, Function.identity()));
        return mbdBalanceRateDataMapper.lambdaQuery()
                .in(MbdBalanceRateData::getOrderId, orderIds)
                .list()
                .stream()
                .sorted(Comparator.comparing(MbdBalanceRateData::getCreateDateTime).reversed())
                .map(entity -> {
                    MbdBalanceRateDataListOfFactoryResVo itemVo = new MbdBalanceRateDataListOfFactoryResVo();
                    itemVo.setId(entity.getId());
                    itemVo.setSaveTime(entity.getCreateDateTime().format(DateTimeFormatter.ofPattern(" yyyy-MM-dd HH:mm:ss")));
                    itemVo.setDimensionType(entity.getDimensionType());
                    itemVo.setMaterName(orderMap.get(entity.getOrderId()).getMaterName());
                    return itemVo;
                })
                .collect(Collectors.toList());
    }

    public List<MbdBalanceRateOfRobotWeldingWireResVo> listRobotWeldingWireUsage(Integer orderId) {
        Order order = orderMapper.selectOneAll(orderId);
        Pattern pattern = Pattern.compile("^(?<plmidPrefix>.+)P([123456]|DB)$");
        Matcher matcher = pattern.matcher(order.getMaterCode());
        if (!matcher.matches()) {
            throw SyExceptionUtils.e("不支持计算焊丝平衡率");
        }
        String plmidPrefix = matcher.group("plmidPrefix");
        List<RobotWeldingWireUsageVo> weldingWireUsage = momSrvMbdApiClient.listRobotWeldingWireUsage(plmidPrefix).getData();
        List<MbdBalanceRateOfRobotWeldingWireResVo> result = new ArrayList<>();
        for (RobotWeldingWireUsageVo weldingWireUsageVo : weldingWireUsage) {
            MbdBalanceRateOfRobotWeldingWireResVo item = new MbdBalanceRateOfRobotWeldingWireResVo();
            result.add(item);
            item.setStage(weldingWireUsageVo.getProcessRouteNumber().substring(plmidPrefix.length()).replaceAll("M?\\(170\\)", ""));
            item.setPhaseName(weldingWireUsageVo.getOperationName());
            item.setJ1(weldingWireUsageVo.getJ1());
            item.setJ2(weldingWireUsageVo.getJ2());
            item.setJ3(weldingWireUsageVo.getJ3());
            item.setJ4(weldingWireUsageVo.getJ4());
            BigDecimal[] aggregateData = calculateRobotWeldingAgvMax(item);
            BigDecimal agv = aggregateData[0];
            BigDecimal max = aggregateData[1];
            if (max.compareTo(BigDecimal.ZERO) == 0) {
                item.setBalanceRate(BigDecimal.ZERO);
                continue;
            }
            item.setBalanceRate(agv.divide(max, 2, RoundingMode.HALF_UP));
        }
        return result;
    }

    private BigDecimal[] calculateRobotWeldingAgvMax(MbdBalanceRateOfRobotWeldingWireResVo item) {
        List<BigDecimal> jList = Stream.of(item.getJ1(), item.getJ2(), item.getJ3(), item.getJ4())
                .filter(StringUtils::isNotBlank)
                .map(BigDecimal::new)
                .filter(bigDecimal -> bigDecimal.compareTo(BigDecimal.ZERO) > 0)
                .collect(Collectors.toList());
        if (jList.isEmpty()) {
            return new BigDecimal[]{BigDecimal.ZERO, BigDecimal.ZERO};
        }
        int count = jList.size();
        if (item.getPhaseName() != null && item.getPhaseName().contains("立焊")) {
            count = 4;
        }
        BigDecimal avg = jList.stream().reduce(BigDecimal::add).orElse(BigDecimal.ZERO).divide(new BigDecimal(count), 4, RoundingMode.HALF_UP);
        BigDecimal max = jList.stream().reduce(BigDecimal::max).orElse(BigDecimal.ZERO);
        return new BigDecimal[]{avg, max};
    }

    @Transactional(readOnly = true)
    public List<MbdPlanMonthlyResVo> monthly(MbdPlanMonthlyReqVo reqVo) {
        if (StringUtils.isBlank(reqVo.getMonth())) {
            throw SyExceptionUtils.e("月份不能为空");
        }
        YearMonth month = YearMonth.parse(reqVo.getMonth(), DatePattern.NORM_MONTH_FORMATTER);
        LocalDate startDate = month.atDay(1);
        LocalDate endDate = startDate.with(TemporalAdjusters.lastDayOfMonth());
        long until = startDate.until(endDate, ChronoUnit.DAYS);
        List<LocalDate> allDate = Stream.iterate(startDate, s -> s.plusDays(1)).limit(until + 1).collect(Collectors.toList());

        List<Order> orders = orderMapper.lambdaQuery()
                .in(Order::getOrderType, 2,5)
                .in(CollUtil.isNotEmpty(reqVo.getSuffix()), Order::getSuffix, reqVo.getSuffix())
                .eq(Order::getPlannedMonth, month.format(DateTimeFormatter.ofPattern("yyMM")))
                .like(StringUtils.isNotBlank(reqVo.getPlmId()), Order::getPlmId, reqVo.getPlmId())
                .like(StringUtils.isNotBlank(reqVo.getConstructNo()), Order::getConstructNo, reqVo.getConstructNo())
                .orderByAsc(Order::getSeq)
                .list();

//        List<Order> orders = orderMapper.selectBatchIds(Arrays.asList(781, 782, 783, 784));

        if (orders.isEmpty()) {
            return new ArrayList<>();
        }
        List<Integer> orderIds = orders.stream().map(Order::getId).collect(Collectors.toList());
        // 赋值 项目和部件
        List<MbdPlanMonthlyResVo> resVoList = new ArrayList<>();

        // 下料工序，总计划，已完成，剩余
        QueryWrapper<ApsWorkDetail> cutWrapper = Wrappers.query();
        cutWrapper.in("  awd.phase_code ", "JG", "HG");
        cutWrapper.eq("  awd.type ", 2);
        cutWrapper.like(" awo.assembly_path ", "P1");
        cutWrapper.in(" awd.order_id ", orderIds);
        cutWrapper.groupBy(" awd.order_id, awo.bom_id, awo.plm_id, DATE_FORMAT(DATE_SUB(awd.plan_start_date_time, INTERVAL 7 HOUR), '%Y-%m-%d') ");
        List<PlanMonthlyProcessDateResVo> cutList = apsWorkDetailMapper.cutList(cutWrapper);
        for (PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo : cutList) {
            planMonthlyProcessDateResVo.setDate(LocalDate.parse(planMonthlyProcessDateResVo.getDateStr(), DatePattern.NORM_DATE_FORMATTER));
        }
        Map<Integer, List<PlanMonthlyProcessDateResVo>> orderIdDateCutMap = cutList.stream().collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getOrderId));

        QueryWrapper<ApsWorkDetail> cutOtherWrapper = Wrappers.query();
        cutOtherWrapper.in("  awd.phase_code ", "JG", "HG");
        cutOtherWrapper.eq("  awd.type ", 2);
        cutOtherWrapper.and(w -> {
            w.like(" o.mater_name ", "侧板");
            w.or();
            w.like(" o.mater_name ", "连杆");
        });
        cutOtherWrapper.in(" awd.order_id ", orderIds);
        cutOtherWrapper.groupBy("  awd.order_id, awo.bom_id, awo.plm_id, DATE_FORMAT(DATE_SUB(awd.plan_start_date_time, INTERVAL 7 HOUR), '%Y-%m-%d') ");

        List<PlanMonthlyProcessDateResVo> cutOtherList = apsWorkDetailMapper.cebanCutList(cutOtherWrapper);
        for (PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo : cutOtherList) {
            planMonthlyProcessDateResVo.setDate(LocalDate.parse(planMonthlyProcessDateResVo.getDateStr(), DatePattern.NORM_DATE_FORMATTER));
        }
        Map<Integer, List<PlanMonthlyProcessDateResVo>> orderIdDateCutOtherMap = cutOtherList.stream().collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getOrderId));


//        QueryWrapper<ApsWorkDetail> completeWrapper = Wrappers.query();
//        completeWrapper.in("  awd.phase_code ", "KJ");
//        completeWrapper.eq("  awd.type ", 2);
//        completeWrapper.like("  awo2.plm_id ", "P1");
//        completeWrapper.in(" awd.order_id ", orderIds);
//        completeWrapper.groupBy("  awd.order_id, awo.bom_id, awo.plm_id, DATE_FORMAT(awd.plan_start_date_time, '%Y-%m-%d') ");
//        List<PlanMonthlyProcessDateResVo> completeList = apsWorkDetailMapper.kjList(completeWrapper);


        QueryWrapper<ApsWorkOrder> assemblyWrapper = Wrappers.query();
        assemblyWrapper.ne("  awo.work_chain ", "WL");
        assemblyWrapper.in(" awo.order_id ", orderIds);
        assemblyWrapper.like(" awo2.plm_id ", "P1");
        assemblyWrapper.groupBy("  awo.order_id, awo.bom_id, awo.bom_pid, awo.plm_id, DATE_FORMAT(DATE_SUB(awd.plan_start_date_time, INTERVAL 7 HOUR), '%Y-%m-%d') ");
        List<PlanMonthlyProcessDateResVo> completeList = apsWorkOrderMapper.partsAssemblyAreaMbd(assemblyWrapper, orderIds.stream().map(Object::toString).collect(Collectors.joining(",")));
        for (PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo : completeList) {
            planMonthlyProcessDateResVo.setDate(LocalDate.parse(planMonthlyProcessDateResVo.getDateStr(), DatePattern.NORM_DATE_FORMATTER));
        }
        Map<Integer, List<PlanMonthlyProcessDateResVo>> orderIdDateCompleteMap = completeList.stream().collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getOrderId));


        QueryWrapper<ApsWorkDetail> weldingWrapper = Wrappers.query();
        weldingWrapper.in("  awd.phase_code ", "WH");
        weldingWrapper.eq("  awd.type ", 2);
        weldingWrapper.in(" awd.order_id ", orderIds);
        weldingWrapper.groupBy("  awd.order_id, awo.bom_id, awo.plm_id, DATE_FORMAT(awd.plan_start_date_time, '%Y-%m-%d') ");
        List<PlanMonthlyProcessDateResVo> weldingList = apsWorkDetailMapper.whList(weldingWrapper);
        for (PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo : weldingList) {
            planMonthlyProcessDateResVo.setDate(LocalDate.parse(planMonthlyProcessDateResVo.getDateStr(), DatePattern.NORM_DATE_FORMATTER));
        }
        Map<Integer, List<PlanMonthlyProcessDateResVo>> orderIdDateWeldingMap = weldingList.stream().collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getOrderId));


        QueryWrapper<ApsWorkDetail> sprayingWrapper = Wrappers.query();
        sprayingWrapper.in("  awd.phase_code ", "PT");
        sprayingWrapper.eq("  awd.type ", 2);
        sprayingWrapper.in(" awd.order_id ", orderIds);
        sprayingWrapper.groupBy("  awd.order_id, awo.bom_id, awo.plm_id, DATE_FORMAT(awd.plan_start_date_time, '%Y-%m-%d') ");
        List<PlanMonthlyProcessDateResVo> sprayingList = apsWorkDetailMapper.whList(sprayingWrapper);
        for (PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo : sprayingList) {
            planMonthlyProcessDateResVo.setDate(LocalDate.parse(planMonthlyProcessDateResVo.getDateStr(), DatePattern.NORM_DATE_FORMATTER));
        }
        Map<Integer, List<PlanMonthlyProcessDateResVo>> orderIdDateSprayingMap = sprayingList.stream().collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getOrderId));

        List<String> dajian = Arrays.asList("D", "Z", "Y");
        List<String> xiaojian = Arrays.asList("C", "L", "T", "B");
        List<Integer> dajianOrderId = orders.stream().filter(e -> e.getOrderType().equals(1) && e.getSuffix() != null && dajian.contains(e.getSuffix())).map(BaseEntity::getId).collect(Collectors.toList());
        List<Integer> xiaojianOrderId = orders.stream().filter(e -> e.getOrderType().equals(1) && e.getSuffix() != null && xiaojian.contains(e.getSuffix())).map(BaseEntity::getId).collect(Collectors.toList());


        List<PlanMonthlyProcessDateResVo> completeCbList = new ArrayList<>();
        if(CollUtil.isNotEmpty(xiaojianOrderId)){
            QueryWrapper<ApsWorkDetail> completeCbWrapper = Wrappers.query();
            completeCbWrapper.in("  awd.phase_code ", "KJ");
            completeCbWrapper.eq("  awd.type ", 2);
            completeCbWrapper.like("  awo.level ", 3);
            completeCbWrapper.in(" awd.order_id ", xiaojianOrderId);
            completeCbWrapper.groupBy("  awd.order_id, awo.bom_id, awo.plm_id, DATE_FORMAT(awd.plan_start_date_time, '%Y-%m-%d') ");
            completeCbList = apsWorkDetailMapper.kjList(completeCbWrapper);
            for (PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo : completeCbList) {
                planMonthlyProcessDateResVo.setDate(LocalDate.parse(planMonthlyProcessDateResVo.getDateStr(), DatePattern.NORM_DATE_FORMATTER));
            }
        }
        Map<Integer, List<PlanMonthlyProcessDateResVo>> orderIdCbDateCompleteMap = completeCbList.stream().collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getOrderId));


        List<OrderBom> dajianBom = new ArrayList<>();
        if (CollUtil.isNotEmpty(dajianOrderId)) {
            dajianBom = orderBomMapper.lambdaQuery().eq(OrderBom::getLevel, 1).in(OrderBom::getOrderId, dajianOrderId).list();
        }
        List<OrderBom> xiaojianBom = new ArrayList<>();
        if (CollUtil.isNotEmpty(xiaojianOrderId)) {
            xiaojianBom = orderBomMapper.lambdaQuery().in(OrderBom::getOrderId, xiaojianOrderId).list();
        }

        Map<Integer, BigDecimal> materWeightMap = new HashMap<>();
        for (OrderBom orderBom : dajianBom) {
            materWeightMap.put(orderBom.getOrderId(), orderBom.getMaterWeight());
        }

        List<OrderBom> level2Bom = xiaojianBom.stream().filter(e -> e.getLevel() == 2).collect(Collectors.toList());
        for (OrderBom orderBom : level2Bom) {
            materWeightMap.compute(orderBom.getOrderId(), (k, v) -> v == null ? orderBom.getMaterWeight() : v.add(orderBom.getMaterWeight()));
        }
        Map<Integer, List<OrderBom>> orderIdMap = xiaojianBom.stream().collect(Collectors.groupingBy(OrderBom::getOrderId));

        for (Order order : orders) {
            MbdPlanMonthlyResVo resVo = new MbdPlanMonthlyResVo();
            resVo.setOrderId(order.getId());
            resVo.setConstructNo(order.getConstructNo());
            resVo.setMaterName(order.getMaterName().replaceAll("[一二三四五六]拼", ""));
            resVo.setFrameType(order.getFrameType());
            resVo.setPlannedMonth(order.getPlannedMonth());
            resVo.setSeq(order.getSeq());
            if(order.getFinishQty() == null){
                order.setFinishQty(0);
            }
            if (order.getMaterName().equals("侧板") || order.getMaterName().equals("连杆")) {

                List<OrderBom> jgOrderBomList = orderIdMap.getOrDefault(order.getId(), new ArrayList<>()).stream().filter(e -> e.getWorkChain().contains("JG") || e.getWorkChain().contains("HG")).collect(Collectors.toList());

                MbdPlanMonthlyResVo.PhaseData cutProcessResVo = new MbdPlanMonthlyResVo.PhaseData();
                cutProcessResVo.setProcess("下料");
                resVo.getProcessInfoList().add(cutProcessResVo);
                List<MbdPlanMonthlyResVo.DayData> cutDateQuantityList = getCeBanPinHan(allDate, startDate, endDate, orderIdDateCutOtherMap.getOrDefault(order.getId(), new ArrayList<>()), cutProcessResVo, order, jgOrderBomList);
                cutProcessResVo.getDateQuantityList().addAll(cutDateQuantityList);
                cutProcessResVo.setPlanCount(new BigDecimal(cutDateQuantityList.stream().map(MbdPlanMonthlyResVo.DayData::getQuantity).reduce(0, Integer::sum)));
                cutProcessResVo.setFinishCount(new BigDecimal(0));
                cutProcessResVo.setTotalCount(new BigDecimal(order.getOrderQty()));
                cutProcessResVo.setTodoCount(cutProcessResVo.getPlanCount().subtract(cutProcessResVo.getFinishCount()));
                if (cutProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    cutProcessResVo.setPlanFinishRate(cutProcessResVo.getFinishCount().divide(cutProcessResVo.getPlanCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }

                List<OrderBom> kjOrderBomList = orderIdMap.getOrDefault(order.getId(), new ArrayList<>()).stream().filter(e -> e.getLevel() == 3 && !e.getMaterType().equals("Component")).collect(Collectors.toList());
                MbdPlanMonthlyResVo.PhaseData completeProcessResVo = new MbdPlanMonthlyResVo.PhaseData();
                completeProcessResVo.setProcess("配套");
                resVo.getProcessInfoList().add(completeProcessResVo);
                List<MbdPlanMonthlyResVo.DayData> completeDateQuantityList = getCeBanPinHan(allDate, startDate, endDate, orderIdCbDateCompleteMap.getOrDefault(order.getId(), new ArrayList<>()), completeProcessResVo, order, kjOrderBomList);
                completeProcessResVo.getDateQuantityList().addAll(completeDateQuantityList);
                completeProcessResVo.setPlanCount(new BigDecimal(cutDateQuantityList.stream().map(MbdPlanMonthlyResVo.DayData::getQuantity).reduce(0, Integer::sum)));
                completeProcessResVo.setFinishCount(new BigDecimal(0));
                completeProcessResVo.setTotalCount(new BigDecimal(order.getOrderQty()));
                completeProcessResVo.setTodoCount(completeProcessResVo.getPlanCount().subtract(completeProcessResVo.getFinishCount()));
                if (completeProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    completeProcessResVo.setPlanFinishRate(completeProcessResVo.getFinishCount().divide(completeProcessResVo.getPlanCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }

                MbdPlanMonthlyResVo.PhaseData weldingProcessResVo = new MbdPlanMonthlyResVo.PhaseData();
                weldingProcessResVo.setProcess("拼焊");
                resVo.getProcessInfoList().add(weldingProcessResVo);

                List<OrderBom> level2CebanBom = orderIdMap.getOrDefault(order.getId(), new ArrayList<>()).stream().filter(e -> e.getLevel() == 2).collect(Collectors.toList());
                List<MbdPlanMonthlyResVo.DayData> weldingDateQuantityList = getCeBanPinHan(allDate, startDate, endDate, orderIdDateWeldingMap.getOrDefault(order.getId(), new ArrayList<>()), weldingProcessResVo, order, level2CebanBom);
                weldingProcessResVo.getDateQuantityList().addAll(weldingDateQuantityList);
                weldingProcessResVo.setPlanCount(new BigDecimal(weldingDateQuantityList.stream().map(MbdPlanMonthlyResVo.DayData::getQuantity).reduce(0, Integer::sum)));
                weldingProcessResVo.setFinishCount(new BigDecimal(order.getFinishQty()));
                weldingProcessResVo.setTotalCount(new BigDecimal(order.getOrderQty()));
                weldingProcessResVo.setTodoCount(weldingProcessResVo.getTotalCount().subtract(weldingProcessResVo.getFinishCount()));
                if (weldingProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    weldingProcessResVo.setPlanFinishRate(weldingProcessResVo.getFinishCount().divide(weldingProcessResVo.getPlanCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }

                MbdPlanMonthlyResVo.PhaseData sprayingProcessResVo = new MbdPlanMonthlyResVo.PhaseData();
                sprayingProcessResVo.setProcess("喷粉");
                resVo.getProcessInfoList().add(sprayingProcessResVo);
                List<MbdPlanMonthlyResVo.DayData> sprayingDateQuantityList = getCeBanPinHan(allDate, startDate, endDate, orderIdDateSprayingMap.getOrDefault(order.getId(), new ArrayList<>()), sprayingProcessResVo, order, level2CebanBom);
                sprayingProcessResVo.getDateQuantityList().addAll(sprayingDateQuantityList);
                sprayingProcessResVo.setPlanCount(new BigDecimal(sprayingDateQuantityList.stream().map(MbdPlanMonthlyResVo.DayData::getQuantity).reduce(0, Integer::sum)));
                sprayingProcessResVo.setFinishCount(new BigDecimal(order.getFinishQty()));
                sprayingProcessResVo.setTotalCount(new BigDecimal(order.getOrderQty()));
                sprayingProcessResVo.setTodoCount(sprayingProcessResVo.getTotalCount().subtract(sprayingProcessResVo.getFinishCount()));
                if (sprayingProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    sprayingProcessResVo.setPlanFinishRate(sprayingProcessResVo.getFinishCount().divide(sprayingProcessResVo.getTotalCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }


            } else {
                MbdPlanMonthlyResVo.PhaseData cutProcessResVo = new MbdPlanMonthlyResVo.PhaseData();
                cutProcessResVo.setProcess("下料");
                resVo.getProcessInfoList().add(cutProcessResVo);
                List<MbdPlanMonthlyResVo.DayData> cutDateQuantityList = genPzChildData(allDate, startDate, endDate, orderIdDateCutMap.get(order.getId()), cutProcessResVo, order);
                cutProcessResVo.getDateQuantityList().addAll(cutDateQuantityList);
                cutProcessResVo.setPlanCount(new BigDecimal(cutDateQuantityList.stream().map(MbdPlanMonthlyResVo.DayData::getQuantity).reduce(0, Integer::sum)));
                cutProcessResVo.setFinishCount(new BigDecimal(order.getFinishQty()));
                cutProcessResVo.setTodoCount(cutProcessResVo.getTotalCount().subtract(cutProcessResVo.getFinishCount()));
                if (cutProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    cutProcessResVo.setPlanFinishRate(cutProcessResVo.getFinishCount().divide(cutProcessResVo.getPlanCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }

                MbdPlanMonthlyResVo.PhaseData completeProcessResVo = new MbdPlanMonthlyResVo.PhaseData();
                completeProcessResVo.setProcess("配套");
                resVo.getProcessInfoList().add(completeProcessResVo);
                List<MbdPlanMonthlyResVo.DayData> completeDateQuantityList = genPzChildData(allDate, startDate, endDate, orderIdDateCompleteMap.get(order.getId()), completeProcessResVo, order);
                completeProcessResVo.getDateQuantityList().addAll(completeDateQuantityList);
                completeProcessResVo.setPlanCount(new BigDecimal(completeDateQuantityList.stream().map(MbdPlanMonthlyResVo.DayData::getQuantity).reduce(0, Integer::sum)));
                completeProcessResVo.setFinishCount(new BigDecimal(order.getFinishQty()));
                completeProcessResVo.setTodoCount(completeProcessResVo.getTotalCount().subtract(completeProcessResVo.getFinishCount()));
                if (completeProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    completeProcessResVo.setPlanFinishRate(completeProcessResVo.getFinishCount().divide(completeProcessResVo.getPlanCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }
                MbdPlanMonthlyResVo.PhaseData weldingProcessResVo = new MbdPlanMonthlyResVo.PhaseData();
                weldingProcessResVo.setProcess("拼焊");
                resVo.getProcessInfoList().add(weldingProcessResVo);
                List<MbdPlanMonthlyResVo.DayData> weldingDateQuantityList = getSingle(allDate, startDate, endDate, orderIdDateWeldingMap.get(order.getId()), weldingProcessResVo, order);
                weldingProcessResVo.getDateQuantityList().addAll(weldingDateQuantityList);
                weldingProcessResVo.setPlanCount(new BigDecimal(weldingDateQuantityList.stream().map(MbdPlanMonthlyResVo.DayData::getQuantity).reduce(0, Integer::sum)));
                weldingProcessResVo.setFinishCount(new BigDecimal(order.getFinishQty()));
                weldingProcessResVo.setTodoCount(weldingProcessResVo.getTotalCount().subtract(weldingProcessResVo.getFinishCount()));
                if (weldingProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    weldingProcessResVo.setPlanFinishRate(weldingProcessResVo.getFinishCount().divide(weldingProcessResVo.getPlanCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }

                MbdPlanMonthlyResVo.PhaseData sprayingProcessResVo = new MbdPlanMonthlyResVo.PhaseData();
                sprayingProcessResVo.setProcess("喷粉");
                resVo.getProcessInfoList().add(sprayingProcessResVo);
                List<MbdPlanMonthlyResVo.DayData> sprayingDateQuantityList = getSingle(allDate, startDate, endDate, orderIdDateSprayingMap.get(order.getId()), sprayingProcessResVo, order);
                sprayingProcessResVo.getDateQuantityList().addAll(sprayingDateQuantityList);
                sprayingProcessResVo.setPlanCount(new BigDecimal(sprayingDateQuantityList.stream().map(MbdPlanMonthlyResVo.DayData::getQuantity).reduce(0, Integer::sum)));
                sprayingProcessResVo.setFinishCount(new BigDecimal(order.getFinishQty()));
                sprayingProcessResVo.setTodoCount(sprayingProcessResVo.getTotalCount().subtract(sprayingProcessResVo.getFinishCount()));
                if (sprayingProcessResVo.getPlanCount().compareTo(BigDecimal.ZERO) > 0) {
                    sprayingProcessResVo.setPlanFinishRate(sprayingProcessResVo.getFinishCount().divide(sprayingProcessResVo.getTotalCount(), 4, RoundingMode.HALF_UP).multiply(new BigDecimal(100)));
                }

            }


            if (order.getOrderType().equals(1)) {
                BigDecimal bigDecimal = materWeightMap.get(order.getId());
                if (bigDecimal != null) {
                    for (MbdPlanMonthlyResVo.PhaseData planMonthlyProcessResVo : resVo.getProcessInfoList()) {
                        planMonthlyProcessResVo.setUnitWeight(bigDecimal);
                        planMonthlyProcessResVo.setPlanWeight(bigDecimal.multiply(new BigDecimal(order.getOrderQty())));
                    }
                }
            }

            resVoList.add(resVo);

        }

        return resVoList;
    }

    /*private MbdPlanMonthlyResVo.PhaseData calculateCompletion(Order order,
                                                              Map<Integer, OrderBom> orderBom,
                                                              List<PlanProDetailDayResVo> data,
                                                              List<String> phaseCodes,
                                                              String phaseName,
                                                              LocalDate startDate,
                                                              LocalDate endDate) {
        List<OrderBom> pivotBomList = orderBom.values().stream()
                .filter(e -> phaseCodes.stream().anyMatch(phaseCode -> e.getWorkChain().contains(phaseCode)))
                .collect(Collectors.toList());
        LocalDate currentDate = startDate;
    }*/

    public List<MbdPlanMonthlyResVo.DayData> getCeBanPinHan(List<LocalDate> allDate, LocalDate startDate, LocalDate endDate, List<PlanMonthlyProcessDateResVo> planMonthlyProcessDateResVos, MbdPlanMonthlyResVo.PhaseData weldingProcessResVo, Order order, List<OrderBom> orderBoms) {

        // 总数量
        int yestdayCount = 0;
        List<MbdPlanMonthlyResVo.DayData> collect = new ArrayList<>();
        for (LocalDate date : allDate) {
            MbdPlanMonthlyResVo.DayData calendarResVo = new MbdPlanMonthlyResVo.DayData();
            calendarResVo.setDate(date);
            int dayCount = calcMatchCount(planMonthlyProcessDateResVos.stream().filter(e1 -> e1 != null && date != null && e1.getDate() != null && !date.isBefore(e1.getDate())).collect(Collectors.toList()), orderBoms);
            calendarResVo.setQuantity(dayCount - yestdayCount);
            calendarResVo.setFinishCount(dayCount);
            yestdayCount = dayCount;
            collect.add(calendarResVo);
        }

        return collect;
    }

    public Integer calcMatchCount(List<PlanMonthlyProcessDateResVo> planMonthlyProcessDateResVos, List<OrderBom> orderBoms) {
        Map<Integer, List<PlanMonthlyProcessDateResVo>> bomIdMap = planMonthlyProcessDateResVos.stream().collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getBomId));

        Optional<BigDecimal> min = orderBoms.stream()
                .map(e -> {
                    List<PlanMonthlyProcessDateResVo> planProDetailDayResVos = bomIdMap.get(e.getId());
                    if (CollUtil.isEmpty(planProDetailDayResVos)) {
                        return new BigDecimal(0);
                    }
                    return planProDetailDayResVos.stream()
                            .filter(e1 -> e1.getTotalCount() != null)
                            .map(PlanMonthlyProcessDateResVo::getTotalCount)
                            .reduce(BigDecimal.ZERO, BigDecimal::add)
                            .divide(e.getTotalCount(), 0, RoundingMode.DOWN);
                })
                .min(BigDecimal::compareTo);
        return min.map(BigDecimal::intValue).orElse(0);
    }

    public List<MbdPlanMonthlyResVo.DayData> getSingle(List<LocalDate> allDate, LocalDate startDate, LocalDate endDate, List<PlanMonthlyProcessDateResVo> planMonthlyProcessDateResVos, MbdPlanMonthlyResVo.PhaseData cutProcessResVo, Order order) {

        if (CollUtil.isEmpty(planMonthlyProcessDateResVos)) {
            cutProcessResVo.setTotalCount(BigDecimal.ZERO);
            cutProcessResVo.setFinishCount(BigDecimal.ZERO);
            return new ArrayList<>();
        }

        // 总数量
        PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo = planMonthlyProcessDateResVos.stream().min(Comparator.comparing(PlanMonthlyProcessDateResVo::getParentPartCount)).get();
        BigDecimal root = planMonthlyProcessDateResVo.getParentPartCount().multiply(new BigDecimal(order.getOrderQty()));
        cutProcessResVo.setTotalCount(root);

        // 完成数量
        BigDecimal finishCount = planMonthlyProcessDateResVos.stream().filter(e -> LocalDate.parse(e.getDateStr(), DatePattern.NORM_DATE_FORMATTER).isBefore(startDate)).map(PlanMonthlyProcessDateResVo::getTotalCount).reduce(BigDecimal.ZERO, BigDecimal::add);
        cutProcessResVo.setFinishCount(finishCount);

        // 之后每一天的
        List<MbdPlanMonthlyResVo.DayData> collect = allDate.stream()
                .map(e -> {
                    MbdPlanMonthlyResVo.DayData calendarResVo = new MbdPlanMonthlyResVo.DayData();
                    calendarResVo.setDate(e);
                    return calendarResVo;
                })
                .sorted(Comparator.comparing(MbdPlanMonthlyResVo.DayData::getDate))
                .collect(Collectors.toList());

        int sum = 0;
        for (MbdPlanMonthlyResVo.DayData currentDay : collect) {
            BigDecimal reduce = planMonthlyProcessDateResVos.stream().filter(e1 -> LocalDate.parse(e1.getDateStr(), DatePattern.NORM_DATE_FORMATTER).equals(currentDay.getDate())).map(PlanMonthlyProcessDateResVo::getTotalCount).reduce(BigDecimal.ZERO, BigDecimal::add);
            currentDay.setQuantity(reduce.intValue());
            sum += reduce.intValue();
            currentDay.setFinishCount(sum);
        }

        return collect;
    }

    public List<MbdPlanMonthlyResVo.DayData> genPzChildData(List<LocalDate> allDate, LocalDate startDate, LocalDate endDate, List<PlanMonthlyProcessDateResVo> planMonthlyProcessDateResVos, MbdPlanMonthlyResVo.PhaseData cutProcessResVo, Order order) {

        if (CollUtil.isEmpty(planMonthlyProcessDateResVos)) {
            cutProcessResVo.setTotalCount(BigDecimal.ZERO);
            cutProcessResVo.setFinishCount(BigDecimal.ZERO);
            return new ArrayList<>();
        }

        // 总数量
        PlanMonthlyProcessDateResVo planMonthlyProcessDateResVo = planMonthlyProcessDateResVos.stream().min(Comparator.comparing(PlanMonthlyProcessDateResVo::getParentPartCount)).get();
        BigDecimal root = planMonthlyProcessDateResVo.getParentPartCount().multiply(new BigDecimal(order.getOrderQty()));
        cutProcessResVo.setTotalCount(root);

        // 比例
        Map<String, BigDecimal> biliMap = planMonthlyProcessDateResVos.stream().collect(Collectors.toMap(PlanMonthlyProcessDateResVo::getPlmId, PlanMonthlyProcessDateResVo::getPartCount, (e1, e2) -> e1));

        // 完成数量
        Map<String, BigDecimal> finishCount = planMonthlyProcessDateResVos.stream().filter(e -> LocalDate.parse(e.getDateStr(), DatePattern.NORM_DATE_FORMATTER).isBefore(startDate)).collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getPlmId, Collectors.reducing(BigDecimal.ZERO, PlanMonthlyProcessDateResVo::getTotalCount, BigDecimal::add)));
        BigDecimal bigDecimal1 = biliMap.entrySet()
                .stream()
                .map(e -> {
                    BigDecimal bigDecimal = finishCount.get(e.getKey());
                    if (bigDecimal == null) {
                        return new BigDecimal(0);
                    }
                    if (bigDecimal.intValue() == 0) {
                        return new BigDecimal(0);
                    }
                    return bigDecimal.divide(e.getValue(), 2, RoundingMode.DOWN);
                })
                .min(BigDecimal::compareTo)
                .orElseGet(() -> new BigDecimal(0));

        cutProcessResVo.setFinishCount(bigDecimal1);

        // 之后每一天的
        List<MbdPlanMonthlyResVo.DayData> collect = allDate.stream()
                .map(e -> {
                    MbdPlanMonthlyResVo.DayData calendarResVo = new MbdPlanMonthlyResVo.DayData();
                    calendarResVo.setDate(e);
                    return calendarResVo;
                })
                .sorted(Comparator.comparing(MbdPlanMonthlyResVo.DayData::getDate))
                .collect(Collectors.toList());


        Map<LocalDate, Integer> map = new HashMap<>();
        int sum = 0;
        for (MbdPlanMonthlyResVo.DayData currentDay : collect) {
            Map<String, BigDecimal> dayPlmIdCount = planMonthlyProcessDateResVos.stream().filter(e1 -> LocalDate.parse(e1.getDateStr(), DatePattern.NORM_DATE_FORMATTER).equals(currentDay.getDate())).collect(Collectors.groupingBy(PlanMonthlyProcessDateResVo::getPlmId, Collectors.reducing(BigDecimal.ZERO, PlanMonthlyProcessDateResVo::getTotalCount, BigDecimal::add)));

            BigDecimal jiashu = biliMap.entrySet()
                    .stream()
                    .map(e -> {
                        BigDecimal bigDecimal = finishCount.get(e.getKey());
                        if (bigDecimal == null) {
                            bigDecimal = new BigDecimal(0);
                        }

                        BigDecimal dayCount = dayPlmIdCount.getOrDefault(e.getKey(), BigDecimal.ZERO);
                        BigDecimal newFinish = bigDecimal.add(dayCount);
                        finishCount.put(e.getKey(), newFinish);
                        return newFinish.divide(e.getValue(), 0, RoundingMode.DOWN);
                    })
                    .min(BigDecimal::compareTo)
                    .orElseGet(() -> new BigDecimal(0));
            map.put(currentDay.getDate(), jiashu.intValue());

            Integer orDefault = map.getOrDefault(currentDay.getDate().minusDays(1), bigDecimal1.intValue());

            currentDay.setQuantity(jiashu.intValue() - orDefault);
            sum += currentDay.getQuantity();
            currentDay.setFinishCount(sum);

        }

        return collect;
    }
}
